You never handle your customer’s identity documents. You ask for a link, send them the link, and a signed webhook tells you the outcome.
1

Create an application

POST /api/v1/kyc/applications returns a link_url valid for a limited time.
2

Send the link to your customer

SMS, email, in-app — your channel. Nothing else is required of you.
3

They complete it on their phone

They photograph their ID and record a short video. Those images go straight to the verification service; they never pass through your integration.
4

You receive the verdict

A KYC_COMPLETED, KYC_REJECTED, KYC_NEEDS_REVIEW, KYC_FAILED or KYC_EXPIRED webhook at the endpoints you already registered.
This API is asynchronous end to end. Creating an application returns as soon as a link exists — long before your customer has done anything with it. There is no synchronous variant: a verification takes as long as the applicant takes, which is unbounded.

Create an application

Treat link_url as a secret. Anyone holding it can submit identity documents against this application. Send it over a channel you would send a password reset over — and do not log it, put it in an analytics event, or embed it in a shareable page.

Options worth knowing

Leave require_video on. A single still photograph cannot establish that a live person is present — one frame is indistinguishable from a photograph of a photograph. An application with it turned off can never be auto-approved and will always land in manual review, which is slower for everyone. Turn it off only for a flow where a human confirms the holder some other way.

Handling the result

The webhook arrives at the endpoints you registered under Webhooks — the same signed, retrying channel your payment events use.
KYC_NEEDS_REVIEW is not a soft approval. A possible sanctions match is never cleared automatically — a human has to look at it. Do not onboard the customer, and do not treat the absence of a rejection as a yes. You will receive KYC_REVIEW_RESOLVED when the decision is made, usually within a working day.

Reading applications back

Polling is a fallback, not the design — use the webhook. synced_at on each row tells you when Dinari last heard from the verification service about it, which is the field to look at if you suspect a callback went missing.

What we do and do not hold

The response tells you the decision, the reason codes and non-identifying document facts (type, expiry, nationality). It does not return your customer’s name, document number or images, and neither does the webhook. That is not an omission — Dinari does not store them. They stay inside the verification service and are released only to an authorised compliance operator looking at a specific case. If your own regulator requires you to hold identity documents, collect them under your own obligation; this API is not that channel.
The link stops working immediately and the applicant sees an explanation rather than a dead page. Cancelling one that already completed, expired or was cancelled returns 409 — there is nothing left to revoke.

When KYC is not enabled

If this deployment has no verification service configured, every endpoint here answers 503 with {"error": "kyc_unavailable"}. Payments and the rest of the API are unaffected — the feature is optional, and its absence does not take the platform down.