Verifications
The core resource, one attempt by one person to satisfy one flow
A verification is a single session in which one person works through the checks of one flow. It is
the object you create, the object the user interacts with, and the object every webhook and every
result refers back to. Verification ids start with vs_.
Lifecycle
A verification is created in pending, moves to processing once submissions are in and the checks
are running, and settles on one of three terminal states. A session the user never completes settles
on expired instead.
pending ──▶ processing ──▶ approved
├─▶ rejected
└─▶ manual_review
pending ──▶ expired (the user never finished in time)Statuses covers what each one means and which are safe to act on.
Creating one
curl https://api.trustwix.com/v1/verifications \
-H "Authorization: Bearer TWK_..." \
-H "Content-Type: application/json" \
-H "Idempotency-Key: user-123-onboarding" \
-d '{
"flow": "flw_id_and_liveness",
"applicant": { "reference_id": "your-user-123", "email": "ana@example.com" },
"locale": "es",
"return_url": "https://your-app.example/kyc/done",
"metadata": { "plan": "pro" }
}'| Field | Type | Notes |
|---|---|---|
flow | string | A saved flow id, starting with flw_. Provide this or checks, not both. |
checks | array | An inline list of checks, for one-off compositions. |
locale | string | BCP-47 language tag for the user-facing flow. |
return_url | string | Where the hosted flow sends the user when they finish. |
source | string | Label for the entry point that created this session, up to 100 characters. See below. |
applicant | object | reference_id (your own user id), email, and phone. |
metadata | object | Arbitrary key and value pairs, returned unchanged on every read. |
If you send neither flow nor checks, your default flow runs, and the flow
field on the response tells you which one that was. Only if you have not marked any flow as your
default does a built-in preset run instead, and that preset is document, then liveness, then face
match.
Knowing where a verification came from
One flow is usually shared across more than one entry point: your main site, a subdomain, a mobile app, a campaign landing page that runs for six weeks. Without something on the session to tell them apart, those sessions are indistinguishable once they reach your dashboard, and questions like "how did the French campaign convert" have no answer.
There are three ways a session records its origin, in the order they are used.
Send a source label. This is the one you control, so prefer it. It is free text, trimmed and
stored exactly as sent, and it is never interpreted on our side, so use whatever segmentation key
already makes sense to you.
curl https://api.trustwix.com/v1/verifications \
-H "Authorization: Bearer TWK_..." \
-H "Content-Type: application/json" \
-d '{ "flow": "flw_id_and_liveness", "source": "campaign-fr" }'A source longer than 100 characters is rejected with 400 INVALID_BODY rather than silently
shortened, because a truncated segmentation key is worse than an obvious error.
Or issue one API key per site. Every session records the key that created it, so separate keys give you separate segments with no change to your integration at all. This is the least work if you already provision a key per environment or per property.
Or send nothing. When there is no source, the origin of the create call is recorded from the
request instead, as scheme://host with no path and no query string. That is a fallback, not a
substitute: it identifies the host that called us, which for a server-side integration is your
backend rather than the page the user was on.
All three show on every row in your dashboard, so two integrations sharing one flow stop looking identical.
What you get back
The create response is the only place client_token and hosted_verify_url appear. Store id,
hand exactly one of the two credentials to the user-facing surface, and drop the rest.
| Field | Notes |
|---|---|
id | The verification id. Your reconciliation key. |
status | Lifecycle state, see Statuses. |
mode | sandbox or live, derived from the key you used. |
verdict | approve, reject, review, or null until the session settles. |
score | The roll-up across every check that ran, 0 to 1, or null while nothing has resolved. |
reason_codes | Why the verdict is what it is. Empty on a clean approval. |
checks | One entry per check in the flow, each with its own status, verdict and score. |
locale | The language the flow was presented in, if one was set. |
metadata | Whatever you sent on create, returned unchanged. |
client_token | Short-lived credential for an embedded flow. Create response only. |
hosted_verify_url | The hosted page for this session. Create response only. |
expires_at | When an unfinished session will be marked expired. |
Two more fields appear only when you ask for them on a retrieve: applicant, the identity read from
the document, and evidence, the images it was read from. Both need a scope that no key carries by
default. See Applicant data.
Reading one back
curl https://api.trustwix.com/v1/verifications/vs_2f8a1c9b4e7d \
-H "Authorization: Bearer TWK_..."Polling is supported but it is not how you should build. Subscribe to webhooks and use the retrieve endpoint for reconciliation and support lookups.
Add ?include=applicant,documents when you need the identity behind the verdict rather than just the
verdict. See Applicant data.
Listing
GET /v1/verifications is cursor-paginated and newest first. Pass limit up to 100,
starting_after with a verification id to page forward, and status to filter. The response
carries has_more and next_cursor.