Constaia
Endpoints

Sessions and publishable keys

Reference for /v1/sessions and pk_ publishable keys: your backend creates a 15-minute session and the browser uploads each document straight to Constaia with the client_secret, without exposing your secret key.

Sessions (sess_…) let the browser upload documents straight to Constaia, without the file going through your server and without exposing your secret key:

  1. Your backend creates a session with the secret key (ck_…) and decides what is requested and what is checked.
  2. Your page only receives the session's client_secret.
  3. The browser uploads the document to POST /v1/analyze with a publishable key (pk_…) and that client_secret.

Full guide with the widget, fetch and the SDK: Frontend-only integration.

Method and pathKeyWhat it does
POST /v1/sessionssecretCreates a session and returns its client_secret.
GET /v1/sessions/{id}secretRetrieves the session: status and each document's analysis.
POST /v1/analyzepublishableUploads a session document from the browser.
Your server (ck_)                 Browser (pk_)                       Constaia
─────────────────                 ─────────────                       ────────
POST /v1/sessions ──────────────────────────────────────────────────▶ sess_… + client_secret (15 min)
  └─ client_secret ─────────────▶ <constaia-upload> / fetch
                                  POST /v1/analyze ───────────────────▶ analysis with the session's options
GET /v1/sessions/{id} ──────────────────────────────────────────────▶ documents[].analysis_id
GET /v1/analyses/{id}  (or analysis.completed webhook) ─────────────▶ verdict

Publishable keys

A publishable key (pk_live_… or pk_test_…) can ship in your website's code: it only uploads documents to a session your server has already created, and only from the domains you allow.

  • Create them in the dashboard: Developers → API keys, Publishable type. Like every key, it's shown only once.
  • Allowed domains (1–20): app.yoursite.com (that exact host), *.yoursite.com (any subdomain, but not bare yoursite.com: add it separately) and, only on test keys, localhost:3000. The port is part of the host. Domain changes apply immediately.
  • The browser request must carry the Origin header of one of those domains (browsers add it themselves). Otherwise: 403 origin_not_allowed.
  • They only work on POST /v1/analyze with client_secret. Any other route answers 403 publishable_key_not_allowed.
  • The mode comes with the key: a pk_test_… only opens sessions created with a ck_test_…, and a pk_live_…, those created with a ck_live_….

Secret keys, never in the browser

A ck_… key that arrives from a page on another origin (Sec-Fetch-Site header other than same-origin) is rejected with 403 secret_key_in_browser, even if it's valid. If it has already been published, revoke it in the dashboard. See Authentication.

Create a session

POST /v1/sessions
Authorization: Bearer ck_live_…
Content-Type: application/json
FieldTypeDescription
templatestringTemplate (tpl_…) with the analysis options of every document. 422 template_not_found if it doesn't exist.
documentsobject[] (1–10)Documents that can be uploaded. Each one: key (1–40 characters: letters, digits, - or _, unique), label, expect (type or list of types) and checks (as in analyze). Without documents: one, key: "document", with the template's options.
referencestringYour reference (up to 200 characters). It reaches each analysis as metadata.session_reference.
metadataobject string → stringUp to 20 keys. Copied to every analysis. Put the user id here to match the result.
languagees | en | pt | frLanguage of the verdict messages, unless the browser sends another.

Each document's options come from the template and, on top, from the document's expect and checks (checks key by key). This is where you set what the browser must not decide: for example the expected holder (checks.holder) from the authenticated user.

curl https://api.constaia.com/v1/sessions \
  -H "Authorization: Bearer $CONSTAIA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "template": "tpl_01J9Z8Q3K4M5N6P7Q8R9S0T1V2",
    "reference": "user_42",
    "metadata": { "user_id": "42" },
    "documents": [
      { "key": "id_card", "label": "ID card", "expect": ["es_dni", "es_nie"], "checks": { "holder": { "full_name": "María García López" } } }
    ]
  }'
Response (201)
{
  "id": "sess_01J9Z8Q3K4M5N6P7Q8R9S0T1V2",
  "object": "session",
  "client_secret": "sess_01J9Z8Q3K4M5N6P7Q8R9S0T1V2_secret_Xk2…",
  "status": "open",
  "livemode": true,
  "template": "tpl_01J9Z8Q3K4M5N6P7Q8R9S0T1V2",
  "reference": "user_42",
  "metadata": { "user_id": "42" },
  "documents": [
    { "key": "id_card", "label": "ID card", "expect": ["es_dni", "es_nie"], "analysis_id": null, "status": "pending" }
  ],
  "expires_at": "2026-09-30T10:15:00Z",
  "created_at": "2026-09-30T10:00:00Z"
}
FieldDescription
client_secretOnly on creation. Hand it to the page. It grants access to upload this session's documents and nothing else.
statusopen, completed (every document uploaded) or expired (15 minutes passed without completing).
documents[].statuspending (not uploaded), processing (upload in progress) or submitted (it has an analysis).
documents[].analysis_idThe analysis created by the upload, or null.
expires_at15 minutes after creation. Create the session when you render the page, not in advance.

Upload from the browser

POST /v1/analyze
Authorization: Bearer pk_live_…
Content-Type: multipart/form-data
FieldDescription
fileThe document.
client_secretThe session's.
document_keyThe key of the session document. Optional if the session has only one.
optionsOptional. JSON where only language is taken into account. Everything else (expect, checks, storage…) comes from the session and its template.
browser
const form = new FormData();
form.append("file", input.files[0]);
form.append("client_secret", clientSecret);
form.append("document_key", "id_card");

const res = await fetch("https://api.constaia.com/v1/analyze", {
  method: "POST",
  headers: { Authorization: "Bearer pk_live_…" },
  body: form,
});
const analysis = await res.json();

Answers with the analysis object, like a normal call (200, or 202 if it takes more than 30 s), with session_id and document_key. It's charged to your account like any analysis.

  • Each document is analysed once. A second upload of the same document returns 409 document_already_submitted. If the upload fails before the analysis is created (invalid file, no credits, rate limit), the document is freed to try again.
  • There are no progressive results (stream) or Idempotency-Key in this mode.
  • The browser can't read analyses or sessions: the result it sees is for the UI, not for deciding.

Retrieve a session

curl https://api.constaia.com/v1/sessions/sess_01J9Z8Q3K4M5N6P7Q8R9S0T1V2 \
  -H "Authorization: Bearer $CONSTAIA_API_KEY"

Returns the session without client_secret. When the user is done, your backend reads each document's analysis_id here and the analysis with GET /v1/analyses/{id} (or receives it through the analysis.completed webhook), and decides with that verdict. It only sees sessions of the key's mode; if it doesn't exist, 404 resource_missing.

SDKs

Backend and browser
import { Constaia } from "@constaia/sdk";

// Backend (secret key)
const constaia = new Constaia();
const session = await constaia.sessions.create({
  template: "tpl_01J9Z8Q3K4M5N6P7Q8R9S0T1V2",
  reference: "user_42",
  documents: [{ key: "id_card", expect: ["es_dni", "es_nie"] }],
});
// → session.client_secret to the page

// Browser (publishable key): only sessions.submit() is allowed
const browser = new Constaia({ apiKey: "pk_live_…" });
const analysis = await browser.sessions.submit(file, { clientSecret, documentKey: "id_card" });

Errors

HTTPcodeWhen
401session_invalidThe client_secret doesn't exist, is malformed or belongs to another mode or account.
401session_expiredThe session expired (15 minutes). Create another one.
403origin_not_allowedThe page's origin is not in the publishable key's allowed domains.
403publishable_key_not_allowedA pk_ key on a route other than POST /v1/analyze.
403secret_key_in_browserA ck_ key used from a browser.
409document_already_submittedThat session document was already uploaded.
422invalid_document_keydocument_key is not in the session, or it's missing and the session has several documents.
422duplicate_document_keyOn creation: two documents with the same key.
422template_not_foundOn creation: the template doesn't exist (param: "options.template").

These errors' messages are meant to be shown to the person (in es, en, pt or fr). The other upload errors (balance, limits, invalid file) are those of analyze.

Next steps

On this page