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.
| State | Meaning | Card activation |
|---|---|---|
not_started | No attestation exists | Blocked |
pending | You have started verification, no outcome yet | Blocked |
approved | Verified | Permitted |
rejected | Failed definitively | Blocked |
review_required | Needs further evidence | Blocked |
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"
}'
{
"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.
| Field | Why |
|---|---|
provider | The party that performed the check. Your own name if in-house |
verified_at | When you checked, not when you told us. May be in the past, never in the future |
provider_reference | Their identifier for the check. The audit trail hinge |
method | document_and_selfie, document_only, database_check, in_person, other |
expires_at | When re-verification falls due. See re-verification |
notesnotes 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:
| Reported | Event |
|---|---|
approved | cardholder.kyc_approved |
rejected | cardholder.kyc_rejected |
review_required | cardholder.kyc_review_required |
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_statusmatches what you reported GET .../kycreturns the attestation withis_current: true- The matching
cardholder.kyc_*webhook fired - Card activation succeeds after
approved, and returnskyc_not_approvedbefore it
That last pair is the assertion worth automating. It is the actual contract.
Common errors
| Status | code | Fix |
|---|---|---|
422 | kyc_provider_required | provider is mandatory |
422 | kyc_attestation_incomplete | Missing status or verified_at, or verified_at is in the future |
400 | invalid_parameter | not_started cannot be reported |
422 | cardholder_inactive | The cardholder is suspended |
404 | resource_not_found | Wrong cardholder id, or another organization's |