Identity verification

This API is billed separately.

The Identity Verification API proves that a person controls the supplied CPF before a sensitive action. It is independent of account opening and bank accounts: create the journey, give its link to the person, then consume the approval once from your backend.

Prerequisites

  • a master M2M (client_credentials) credential, never a user session or delegated child credential;
  • the X-Tenant-Id header;
  • the identity_verification.manage scope;
  • the identity_verification tenant feature for creation;
  • a callbackUri registered under Identity verifications in the panel, compared exactly.

The credential needs the dedicated kyc.read scope to download KYC evidence.

Create the verification

curl -X POST "https://tenant.api.corpx.com/v1/identity-verifications" \
-H "Authorization: Bearer $TOKEN" \
-H "X-Tenant-Id: $TENANT_ID" \
-H "Content-Type: application/json" \
-d '{
"document": "12345678909",
"purpose": "password_reset",
"referenceId": "usr_01J8Y6N7M8P9Q0R1S2T3",
"displayMessage": "Confirm your identity to reset your password.",
"callbackUri": "https://app.example.com/identity-verification/return",
"ttlMinutes": 30,
"consumeTtlMinutes": 10
}'
FieldRule
documentCPF, exactly 11 digits without punctuation
purposepassword_reset, second_factor_reset, high_value_transaction, or sensitive_action
referenceIdRequired; your action ID and the binding used at consumption
displayMessageOptional plain text, up to 240 characters
callbackUriRegistered URI, exactly as sent
ttlMinutesLink lifetime; default 30, maximum 60
consumeTtlMinutesWindow to consume after approval; default 10, maximum 60

Do not send Idempotency-Key: every POST intentionally creates a fresh proof and consumes one quota unit when it returns 201.

201 response:

{
"verificationId": "idv_01J8Y7C8D9E0F1G2H3J4",
"status": "PENDING",
"verificationLink": "https://tenant.api.corpx.com/v1/identity-verifications/verify#capability-redacted",
"expiresAt": "2026-09-24T14:30:00Z"
}

Give verificationLink only to the person who owns the CPF. Do not treat the return to callbackUri as proof of approval: query the resource or process the webhook, then call the consumption endpoint.

Fixed monthly quota

Each tenant has 10 successful 201 creations per BRT calendar month. Every 201 counts even if the verification later fails, expires, or is never consumed, and quota is never refunded. A closed quota returns HTTP 429 with identity_verification_monthly_limit_exceeded. The rate_limited error also uses HTTP 429, but it is a separate traffic control and does not report monthly usage.

Track the state

curl "https://tenant.api.corpx.com/v1/identity-verifications/$VERIFICATION_ID" \
-H "Authorization: Bearer $TOKEN" \
-H "X-Tenant-Id: $TENANT_ID"
StatusMeaning
PENDINGThe person can still complete the journey; verificationLink is present
APPROVEDIdentity approved; consume by consumableUntil
FAILEDVerification rejected; inspect failureReason
EXPIREDThe journey expired without approval

The response also carries purpose, referenceId, document, provider, expiresAt, and, when applicable, failureReason, completedAt, consumableUntil, and consumedAt. verificationLink appears only while PENDING.

Consume the approval

Consumption binds the proof to the action you are about to execute:

curl -X POST \
"https://tenant.api.corpx.com/v1/identity-verifications/$VERIFICATION_ID/consume" \
-H "Authorization: Bearer $TOKEN" \
-H "X-Tenant-Id: $TENANT_ID" \
-H "Content-Type: application/json" \
-d '{
"purpose": "password_reset",
"referenceId": "usr_01J8Y6N7M8P9Q0R1S2T3"
}'

purpose and referenceId must exactly match creation. The first valid call sets consumedAt. Retrying the exact same verificationId and body is idempotent and returns the same result. A different reference or purpose, a competing consume, a state other than APPROVED, or an elapsed consumption window returns 409.

Keep consumption and the sensitive action in the same logical backend unit. Do not authorize the action merely because GET showed APPROVED: without consumption, concurrent requests could reuse the same proof.

Callback and webhook

callbackUri returns the browser to your app; it is not a server notification. The value must match the tenant registration exactly—no wildcard, prefix matching, or query-string normalization.

For terminal states APPROVED, FAILED, and EXPIRED, the platform sends identity.verification.completed. Verify X-Signature against the raw request body and deduplicate on the envelope id. See the complete payload in Webhooks.

KYC artifacts

curl \
"https://tenant.api.corpx.com/v1/identity-verifications/$VERIFICATION_ID/artifacts" \
-H "Authorization: Bearer $TOKEN" \
-H "X-Tenant-Id: $TENANT_ID"

This route uses kyc.read instead of the management scope and requires the kyc_artifacts feature. The files may contain biometrics and identity material: do not log URLs or contents, and apply your KYC retention policy.

Main errors

HTTPerrorCodeAction
400invalid_document, invalid_purpose, invalid_reference_idCorrect the named field
400invalid_display_message, invalid_verification_ttl, invalid_consume_ttlFollow the documented plain-text and range rules
400invalid_callback_uriSend an exactly registered URI
403feature_disabledRequest feature enablement
403insufficient_scopeUse identity_verification.manage for journeys and kyc.read for artifacts
403forbiddenUse the master M2M credential
404verification_not_foundCheck the ID and X-Tenant-Id
409verification_not_consumableCheck state, window, purpose, and referenceId
409reference_already_consumedAnother proof already consumed that purpose/reference
429identity_verification_monthly_limit_exceededWait for the next BRT calendar month
429rate_limitedReduce request frequency and retry