Identity verification (KYC)
Settlane verifies a customer's identity before it will send them money. This page describes how a merchant drives that from its own server, what each state means, and what stays with an operator.
Identity verification is set up for your account by Settlane. This is switched on per account. Ask support@originstats.com.
The routes
Four routes, under the customers:read and customers:write scopes:
POST /v1/customers/{id}/verificationsstarts a verification and answers itshosted_url, which the customer completes in their own browser. Asked again while that attempt is still pending, it answers the same attempt (200) and opens no second session; asked for an already-verified customer, it answers the approved attempt with no url.GET /v1/customers/{id}/kycis the customer-level answer:state, when it was verified, the latest decided attempt, and whether a new attempt may be started now. It never carries a hosted url and is never cached.GET /v1/verifications/{id}is one attempt, as thecustomer.kyc.updatedevent describes it.?include=document_dob(a key holding bothcustomers:readandcustomers:write) adds the document's date of birth, read live from the provider and never stored — see below.GET /v1/customers/{id}carries the state beside the customer's deposit addresses;PATCH /v1/customers/{id}setsemail,customer_since(YYYY-MM-DDor an ISO instant with a zone, never in the future; every change is audited with the old and new values) andmetadata.
customer.kyc.updated is how you learn a decision was reached. It names the customer (customer_id, external_id), the attempt (verification_id, status) and the customer-level state, so a handler need not re-derive "verified" from an attempt. The API reference lists every field, and describes the event's payload under webhooks.
What stays with an operator, on the dashboard's Customers and Compliance screens: resolving a needs_review attempt, reopening a customer who has used up their attempts, and blocking a customer. A blocked customer can still be read — the KYC read answers retry.blocked_by: customer_blocked — and a verification or a payout for one is refused CUSTOMER_BLOCKED.
The flow
- Your server starts a verification for one customer. Pass
return_url(where the customer's browser lands afterwards) and, if you hold one,expected_date_of_birth: it is compared with the document and then discarded. - The customer completes the hosted check — a document and a liveness check — at
hosted_url. - The provider decides, and Settlane applies its own rules on top of that decision: the date of birth must match what was expected, the age gate must pass, and a duplicate or an AML hit is not an approval. A provider
approvedthat fails one of Settlane's own rules is not an approved verification; it waits for a person asneeds_review. customer.kyc.updatedfires with the outcome.- Payouts to that customer become possible, or do not.
The states
| Status | What it means |
|---|---|
pending | The session exists and the customer has not finished it. decided_at is null exactly while this is the status. |
approved | The provider approved it and every rule Settlane applies afterwards passed. |
declined | Terminal for this attempt. A retry is a new verification, subject to the cool-down and the attempt cap. |
needs_review | A human has to decide. Nothing about the customer changes until they do. |
expired | The provider session expired unused. |
abandoned | The customer started and never came back. |
Those are an attempt's statuses. The customer's state on GET /v1/customers/{id}/kyc is one of verified, pending, needs_review, declined, not_started and unavailable (no provider is configured for your tenant), derived from the latest decided attempt and whether one is pending. latest_verification is that attempt — the row the state was derived from, expired and abandoned included — so the two can never disagree. decided_at is null exactly while the status is pending — the database enforces that equality.
Retries, and the limits on them
A declined verification can be retried, and the limits exist so an attempt cannot be brute-forced:
- a 24-hour cool-down after a failed attempt (
IDENTITY_RETRY_TOO_SOON); - three attempts per 30 days (
IDENTITY_ATTEMPTS_EXHAUSTED); - an under-age decline never retries by itself (
IDENTITY_UNDER_AGE); only an operator's reopen lifts it; - and
IDENTITY_IN_REVIEWwhile an attempt is waiting for a person.
retry on the KYC read says which applies and, for the cool-down, when it ends; customer_blocked is the fourth value, for a customer an operator has blocked.
The document's date of birth
Settlane stores no date of birth. A merchant that needs the document's one — to fill a date of birth it never collected, say — reads it once with GET /v1/verifications/{id}?include=document_dob: it is fetched live from the provider's decision, every such read is audited against your key and counts against the key's create budget, and once the provider session has been deleted (after your endpoint acknowledged customer.kyc.updated, at most seven days later) the read answers VERIFICATION_DOCUMENT_GONE. Only a decided attempt (approved, declined, needs_review) has a document to read: an undecided one, a simulated one (test mode without a sandbox application) or a decision that carries no date of birth answers the attempt without the field.
Test mode
A sk_test_ key verifies against the provider's sandbox when your tenant has one configured, and otherwise against a built-in simulator whose outcome is chosen by the external_id suffix — …_approved, …_declined, …_review. Today a simulated verification stays pending: the part of Settlane that resolves it runs in live mode only, for now.
What a check costs. Each verification that reaches a decision in live mode — approved, declined or sent for review — is billed at your workspace's per-check price, which is on your monthly statement. Sandbox and simulated checks, and attempts that expire or are abandoned, cost nothing.
Verifications you ran before Settlane can be imported, not redone: an operator imports your earlier provider decisions for your live customers (matched by your external_id), and an imported approval counts as a live verification exactly as one made through Settlane does. Imported decisions are never billed and never count against the attempt limit; a person whose attempt was still pending simply starts again here.
A sandbox or simulator result can never satisfy a live verification. Settlane refuses to cross modes here, both in the API and in its database.
Fail-closed, always
Every configuration failure refuses rather than skipping the check:
| Code | State |
|---|---|
IDENTITY_VERIFICATION_UNAVAILABLE | The tenant's provider credentials are missing, incomplete or disabled. |
IDENTITY_PROVIDER_FAILED | The provider threw or answered non-2xx. Retry; nothing about the customer changed. |
IDENTITY_PROVIDER_SCHEMA_UNVERIFIED | Settlane cannot read the provider's decisions safely right now, so it decides nothing. Retry later, and contact support if it lasts. |
Next
- Payouts — what verification a payout needs.
- The errors reference