Frontend-only integration
Upload documents from the browser straight to Constaia with a pk_ publishable key and a session created by your backend: no upload route of your own and no exposed secret key. Widget, fetch and SDK.
With this integration the file doesn't go through your server: the browser uploads it straight to Constaia. Your backend only makes two small calls with the secret key: create the session (what is requested and what is checked) and read the result at the end.
Use it when you don't want to build and protect an upload route (file sizes, limits, antivirus…), on static sites with
a minimal backend, or in desktop and mobile apps that load your website. If you'd rather the file went through your
server, use the widget with endpoint; if you don't even want to build the upload screen, use a
verification link.
Your server (ck_) Browser (pk_) Constaia
───────────────── ───────────── ────────
1. POST /v1/sessions ───────────────────────────────────────────────▶ sess_… + client_secret (15 min)
2. └─ client_secret ──────────▶ page
3. POST /v1/analyze (pk_ + client_secret) ▶ analysis with the session's options
4. GET /v1/sessions/{id} → GET /v1/analyses/{id} (or webhook) ─────▶ verdict: you decide, on the serverReference for every field and error: Sessions and publishable keys.
Step 1: create a publishable key
In the dashboard, Developers → API keys → Create key, choose the Publishable type and add the domains it will be used from:
| Domain | What it allows |
|---|---|
app.yoursite.com | Only that host (and port, if you set one: app.yoursite.com:8443). |
*.yoursite.com | Any subdomain of yoursite.com, not bare yoursite.com (add it separately if you use it). |
localhost:3000 | Your local environment. Test keys only. |
Start with a pk_test_… key and localhost to develop for free. The publishable key can ship in your website's
code: it only uploads documents to sessions your server creates, and only from those domains.
Step 2: create the session on your backend
Your backend decides which documents are requested and what gets checked, usually with a
template and data from the authenticated user (the expected holder, their id in metadata).
Create the session when you render the page: it expires after 15 minutes.
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" } } }
]
}'Never return the secret key or the whole session object to the page: only client_secret.
Step 3: upload from the page
The browser uploads the file to POST https://api.constaia.com/v1/analyze with the publishable key, the
client_secret and the document_key. The analysis options come from the session: only the language is accepted
from the browser.
<script type="module" src="https://cdn.jsdelivr.net/npm/@constaia/widget"></script>
<constaia-upload
publishable-key="pk_test_…"
session-token="{{ clientSecret }}"
document-key="id_card"
document="es_dni"
lang="en"
></constaia-upload>
<script type="module">
document.querySelector("constaia-upload").addEventListener("constaia:result", () => {
// UI only: the verdict that counts is read by your server
fetch("/api/constaia-done", { method: "POST" });
});
</script>With publishable-key, the widget sends file, client_secret, document_key and options with the language;
expect and document only drive the UI (framing, two sides). Use one <constaia-upload> per session document. Every
attribute is in Widget.
The publishable-key attribute is in widget versions after 0.2.0. If your project pins 0.2.0, upload with fetch or
the SDK (the other tabs) until you update.
Step 4: read the result on your server
What the browser sees is for the UI. The decision (signing the user up, approving the registration) is made by your backend with the analysis it reads with its secret key:
const session = await constaia.sessions.get(user.constaiaSessionId);
const doc = session.documents.find((d) => d.key === "id_card");
if (!doc?.analysis_id) return res.status(409).json({ error: "Document missing" });
const analysis = await constaia.analyses.get(doc.analysis_id);
const status = analysis.verdict?.final_status ?? analysis.verdict?.status;You can also wait for the analysis.completed webhook (and analysis.reviewed if there is human
review): the analysis carries session_id, document_key and your metadata, so you can match it with the user
without storing anything else. If the verdict is review, handle it as in Human review.
Test locally
- Create a
ck_test_…key for the backend and apk_test_…key with thelocalhost:3000domain (your dev server's). - Upload the test files (
dni_valid.jpg,dni_expired.jpg…): in test no credits are spent and the result depends on the file name. - Try the errors: upload the same document again (
409 document_already_submitted), wait 15 minutes (401 session_expired) or open the page from another port (403 origin_not_allowed).
Errors the person will see
These errors carry a message meant to be shown to the person (in their language); the widget already shows them.
code | What to do |
|---|---|
session_expired | Create the session again (reload the page). |
session_invalid | Check that you pass the right client_secret, from the same mode as the publishable key. |
document_already_submitted | That document was already uploaded: read the result on your server. |
invalid_document_key | The document_key isn't in the session (or it's missing and there are several documents). |
origin_not_allowed | Add the page's domain to the publishable key in the dashboard. |
Checklist
- The secret key is only on the server; the page only sees
pk_…andclient_secret. expect,checksand the expected holder are set in the session or the template, never in the browser.- You store the
session_id(or put the user id inmetadata) to match the result. - The final decision is made on the server with
GET /v1/analyses/{id}or the webhook. - The domains of the
pk_live_…key are production ones only.
Next steps
Create links via API and receive the results
Create a verification link from your backend, receive the results in a signed callback, send a summary by email and bring the person back to your website with the status.
US documents
The United States document types in the Constaia catalogue, PDF417 (AAMVA) barcode reading on driver's licenses, Form I-9 lists, and validation of SSN, EIN and other identifiers.