Skip to main content

KYC lifecycle and statuses

Report the result of verification you performed, and understand what each state unblocks.

Before you begin​

  • A cardholder — see creating a cardholder.
  • A completed identity check from your own provider. You run verification; we record what you assert. See documents for why nothing is uploaded here.

How it works​

kyc_status is read-only through PATCH. It moves only when you report an outcome, which creates an attestation — a dated, attributed record of who verified this person and how.

StateMeaningCard activation
not_startedNo attestation existsBlocked
pendingYou have started verification, no outcome yetBlocked
approvedVerifiedPermitted
rejectedFailed definitivelyBlocked
review_requiredNeeds further evidenceBlocked

Only approved permits issuing or activating a card. Everything else blocks both with kyc_not_approved.

not_started cannot be reported — it is the absence of an attestation, not an outcome.

Steps​

1. Report an outcome​

curl -X POST "$ALETA_BASE_URL/v1/cardholders/chd_01HX8ZQ2K4M7P9RSTVWXYZAB/kyc" \
-H "Authorization: Bearer $ALETA_ACCESS_TOKEN" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"status": "approved",
"verified_at": "2026-07-29T11:04:00Z",
"provider": "Acme Identity",
"provider_reference": "chk_9f31c7",
"method": "document_and_selfie",
"expires_at": "2028-07-29T00:00:00Z"
}'
Response
{
"id": "kyc_01HX8ZQ2K4M7P9RSTVWXYZAB",
"object": "kyc_attestation",
"cardholder_id": "chd_01HX8ZQ2K4M7P9RSTVWXYZAB",
"status": "approved",
"verified_at": "2026-07-29T11:04:00Z",
"provider": "Acme Identity",
"provider_reference": "chk_9f31c7",
"method": "document_and_selfie",
"expires_at": "2028-07-29T00:00:00Z",
"is_current": true,
"created_at": "2026-07-29T14:32:05Z"
}

2. Supply the fields that make it a record​

provider and verified_at are required, and that is deliberate. An attestation that cannot say who verified and when is a status flag, not evidence. Supply provider_reference too — it is what lets an auditor trace our record back to yours.

FieldWhy
providerThe party that performed the check. Your own name if in-house
verified_atWhen you checked, not when you told us. May be in the past, never in the future
provider_referenceTheir identifier for the check. The audit trail hinge
methoddocument_and_selfie, document_only, database_check, in_person, other
expires_atWhen re-verification falls due. See re-verification
Never put document contents in notes

notes is for context — "manual review by compliance, ticket 4471". It is not for document numbers, images, or extracted identity data. See documents.

3. Read the attestation chain​

curl "$ALETA_BASE_URL/v1/cardholders/chd_01HX8ZQ2K4M7P9RSTVWXYZAB/kyc" \
-H "Authorization: Bearer $ALETA_ACCESS_TOKEN"

Newest first; the first item has is_current: true. Reporting a new outcome supersedes the previous one without deleting it — which is what makes the chain auditable.

4. Expect an echo​

Reporting an outcome fires the matching webhook back to you:

ReportedEvent
approvedcardholder.kyc_approved
rejectedcardholder.kyc_rejected
review_requiredcardholder.kyc_review_required
Yes, you receive an event for something you told us

This looks circular and is intentional. Most clients have more than one system — the service that runs verification is rarely the service that issues cards — and cockpit needs to stay in sync too.

Treat the event as the fan-out mechanism. Your reporting service will receive an echo of its own attestation; deduplicate on the event id and let your other systems act on it.

Gate issuance on the event, not on the response to your own POST. That way one code path handles both your own attestations and any status change originating elsewhere.

Verifying​

  • The cardholder's kyc_status matches what you reported
  • GET .../kyc returns the attestation with is_current: true
  • The matching cardholder.kyc_* webhook fired
  • Card activation succeeds after approved, and returns kyc_not_approved before it

That last pair is the assertion worth automating. It is the actual contract.

Common errors​

StatuscodeFix
422kyc_provider_requiredprovider is mandatory
422kyc_attestation_incompleteMissing status or verified_at, or verified_at is in the future
400invalid_parameternot_started cannot be reported
422cardholder_inactiveThe cardholder is suspended
404resource_not_foundWrong cardholder id, or another organization's