On this page
GET /api/kyc/verifications/:idReturns the full result of a verification, including the extracted biodata, ID number, and facial-match score. This is the backend counterpart to the minimal status endpoint.
Authentication: Authorization: Bearer sk_…, a secret key only. Calling
this with a publishable (pk_) key returns 403 secret_key_required. The
verification is scoped to the key's organisation and environment (a sandbox
secret key cannot read a production verification).
Never call this from client code. Identity data must only be fetched from your backend with a secret key.
Request
curl "https://trust.myaza.app/api/kyc/verifications/ver_01j9..." \
-H "Authorization: Bearer $MYAZA_SECRET_KEY"Responses
Every response includes verificationId, status, country, idType, createdAt,
and workflow, which workflow was responsible for the submission.
approved
{
"verificationId": "ver_01j9...",
"status": "approved",
"checkStatus": "verified",
"externalUserId": "user_42",
"metadata": { "loanId": "loan_20191" },
"country": "NG",
"idType": "bvn",
"createdAt": "2026-04-27T12:00:00.000Z",
"completedAt": "2026-04-27T12:00:05.000Z",
"workflow": {
"id": "wf_AbC123dEf456",
"name": "Standard KYC",
"version": 3
},
"result": {
"idNumber": "12345678901",
"idNumberMasked": "1234•••901",
"firstName": "JOHN",
"lastName": "DOE",
"middleName": "A",
"dateOfBirth": "1990-01-01",
"gender": "Male",
"dataMatch": true,
"facialMatch": { "match": true, "confidence": 85 },
"facialMatchSource": "gov_record"
}
}facialMatch is null when no liveness/selfie check was part of the flow. facialMatchSource says which photo the selfie was compared with: gov_record, chip, or document when the workflow allows the photo printed on the document and no stronger photo existed.
workflow is null when the SDK was configured with plain props instead of a
workflow. Its version is the published version that actually ran: publishing
overwrites a workflow's live configuration in place, so id alone stops describing
what happened after your next publish. Inspect the exact configuration a version
used under Workflows → Version history.
failed / error / not_found
Carries the human-readable reason and stable reasonCode (same values as the
status endpoint and webhooks).
{
"verificationId": "ver_01j9...",
"status": "declined",
"checkStatus": "failed",
"externalUserId": "user_42",
"metadata": { "loanId": "loan_20191" },
"country": "NG",
"idType": "bvn",
"createdAt": "2026-04-27T12:00:00.000Z",
"completedAt": "2026-04-27T12:00:05.000Z",
"reason": "The selfie does not match the photo on the government record for this ID. Take it again in good light, looking straight at the camera. If it still does not match, check that the ID number entered is the right one.",
"reasonCode": "selfie_mismatch"
}After a decision
When the verification has been decided, by a reviewer in your dashboard, by your backend through POST /verifications/:id/review or by your workflow, review holds the current decision a person made and statusHistory lists every change, newest first:
{
"verificationId": "ver_01j9...",
"status": "declined",
"checkStatus": "verified",
"review": {
"decision": "DECLINED",
"reviewedAt": "2026-04-28T09:14:00.000Z",
"reviewedById": "api_key:ak_01j9..."
},
"statusHistory": [
{
"status": "declined",
"previousStatus": "in_review",
"source": "api",
"changedBy": { "type": "api_key", "id": "ak_01j9..." },
"changedAt": "2026-04-28T09:14:00.000Z",
"decision": "DECLINED",
"outcome": null,
"runId": null,
"resubmitSteps": []
},
{
"status": "in_review",
"previousStatus": "processing",
"source": "workflow",
"changedBy": { "type": "workflow", "id": "wf_AbC123dEf456" },
"changedAt": "2026-04-27T12:00:07.000Z",
"decision": null,
"outcome": "review",
"runId": "wfr_01j9...",
"resubmitSteps": []
}
]
}Each entry has the same fields as one verification.status_updated webhook, so what you store from the webhook and what you read here agree. The reviewer's internal note is never included.
Attempts
A verification you send back or verify again keeps its id. Once the applicant resubmits, the result describes their latest attempt, attempt counts up, and each earlier attempt is listed under attempts, oldest first:
{
"verificationId": "ver_01j9...",
"status": "approved",
"attempt": 2,
"submittedAt": "2026-04-29T10:02:00.000Z",
"completedAt": "2026-04-29T10:02:06.000Z",
"attempts": [
{
"attempt": 1,
"checkStatus": "failed",
"reason": "The selfie does not match the photo on the government record for this ID. Take it again in good light, looking straight at the camera.",
"reasonCode": "selfie_mismatch",
"assuranceLevel": null,
"decision": "RESUBMISSION",
"resubmitSteps": ["liveness"],
"submittedAt": "2026-04-27T12:00:00.000Z",
"completedAt": "2026-04-27T12:00:05.000Z",
"resubmittedAt": "2026-04-29T10:02:00.000Z"
}
]
}An earlier attempt carries what it concluded and why, never its biodata or media. The current result is the one to act on, and statusHistory still lists every decision across all attempts.
Result fields
| Field | Type | Description |
|---|---|---|
externalUserId | string | null | Your reference for the subject, exactly as supplied: the top-level externalUserId on the submission (or the legacy userId / metadata.userId), or the session it was minted with. |
metadata | object | null | Your customer metadata, echoed unchanged. |
workflow | object | null | { id, name, version }: the workflow that drove the submission, its current name, and the published version that ran. null for prop-configured SDK mounts. |
review | object | null | The current decision a person or your backend made: { decision, reviewedAt, reviewedById }. reviewedById is a user id, or api_key:<id> for a decision made through the API. null when nobody has decided. |
statusHistory | array | Every change to status, newest first, whoever made it. Empty until something decides the verification. See After a decision. |
attempt | number | Which attempt this result describes. 1 until the verification is sent back and the applicant resubmits. |
submittedAt | string | When this attempt was submitted. completedAt is timed from it. |
attempts | array | The earlier attempts, oldest first. Empty until the verification is redone. See Attempts. |
result.idNumber | string | null | The plaintext ID number. |
result.idNumberMasked | string | The ID number, masked (e.g. 1234•••901). |
result.firstName | string | null | Given name from the source record. |
result.lastName | string | null | Family name from the source record. |
result.middleName | string | null | Middle name, if available. |
result.dateOfBirth | string | null | Date of birth (YYYY-MM-DD). |
result.gender | string | null | Gender from the source record. |
result.dataMatch | boolean | null | Whether submitted biodata matched the source record. |
result.facialMatch | object | null | { match: boolean, confidence: number }, or null if no facial check ran. |
result.facialMatchSource | string | null | Which photo the selfie was compared with: gov_record, chip or document (the photo printed on the ID, weaker evidence). null if no facial check ran. |
result.facialMatchSkipped | string | null | Why no facial check ran when one was expected: no_face_on_document when the photo printed on the document was the only photo to compare with and no face could be read on it (your workflow kept the verification for review). null otherwise. |
Captured media
The images and videos captured during the flow are fetched per-kind from
GET /api/kyc/verifications/:id/media/:kind (selfie, document-front,
document-back, liveness-video, document-front-video, document-back-video,
and document-portrait, the portrait cut from the document when the selfie was matched to it),
also secret key only. These same URLs are delivered in the media object of
verification webhooks.
Errors
| Status | Body | Cause |
|---|---|---|
403 | { "error": "secret_key_required" } | A publishable (pk_) key was used. Use a secret key. |
404 | { "error": "Verification not found" } | Unknown ID, wrong environment, or another organisation's verification. |
401 | { "error": "Invalid API key" } | Auth failed. |