Identity verification

This API is billed separately.

The API creates an account-independent proof for password resets, second-factor resets, high-value transactions, or another sensitive action. The bank backend creates a journey for a CPF, gives the link to the account holder, and consumes the approval once before completing the action.

Use the master, not the account credential

This surface is for the internet-banking master M2M credential. A child credential delegated to one account cannot create, read, or consume verifications. Never send the master credential to a browser or app.

Prerequisites

  • a client_credentials token from the master;
  • the https://client.api.corpx.com host and all three signing headers;
  • X-Tenant-Id;
  • the identity_verification.manage scope;
  • the identity_verification tenant feature for creation;
  • a callbackUri registered under Identity verifications in the panel, compared exactly.

Downloading evidence uses the dedicated kyc.read scope.

Create

curl -X POST "https://client.api.corpx.com/v1/identity-verifications" \
-H "Authorization: Bearer $TOKEN" \
-H "X-Tenant-Id: $TENANT_ID" \
-H "X-Request-Timestamp: $TS" \
-H "X-Content-SHA256: $BODY_SHA256" \
-H "X-Request-Signature: $SIGNATURE" \
-H "Content-Type: application/json" \
-d '{
"document": "12345678909",
"purpose": "second_factor_reset",
"referenceId": "reset_01J8Y6N7M8P9Q0R1S2T3",
"displayMessage": "Confirm your identity to register the new device.",
"callbackUri": "https://ib.example.com/identity-verification/return",
"ttlMinutes": 30,
"consumeTtlMinutes": 10
}'

document is an 11-digit CPF. referenceId is required and identifies the action in your system. Accepted purposes are password_reset, second_factor_reset, high_value_transaction, and sensitive_action. displayMessage is optional plain text up to 240 characters. ttlMinutes defaults to 30 and consumeTtlMinutes to 10; both have a maximum of 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"
}
Ten per tenant, not per account

The fixed quota is 10 201 responses per tenant per BRT calendar month, across every internet-banking journey. Every 201 counts and is never refunded, even if the journey fails, expires, or is not consumed. 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 limit.

Retrieve

curl "https://client.api.corpx.com/v1/identity-verifications/$VERIFICATION_ID" \
-H "Authorization: Bearer $TOKEN" \
-H "X-Tenant-Id: $TENANT_ID" \
-H "X-Request-Timestamp: $TS" \
-H "X-Content-SHA256: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" \
-H "X-Request-Signature: $SIGNATURE"
StatusNext step
PENDINGWait; verificationLink remains present
APPROVEDConsume before consumableUntil
FAILEDDo not execute the action; inspect failureReason
EXPIREDCreate another journey if still needed

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

Consume

curl -X POST \
"https://client.api.corpx.com/v1/identity-verifications/$VERIFICATION_ID/consume" \
-H "Authorization: Bearer $TOKEN" \
-H "X-Tenant-Id: $TENANT_ID" \
-H "X-Request-Timestamp: $TS" \
-H "X-Content-SHA256: $BODY_SHA256" \
-H "X-Request-Signature: $SIGNATURE" \
-H "Content-Type: application/json" \
-d '{
"purpose": "second_factor_reset",
"referenceId": "reset_01J8Y6N7M8P9Q0R1S2T3"
}'

Purpose and reference must exactly match creation. The first valid call writes consumedAt. Retrying the same ID and body is idempotent; a different purpose/reference, competing consumption, non-approved state, or elapsed window returns 409.

Consume from the backend immediately before the protected action. Seeing APPROVED on GET does not reserve the proof and, by itself, does not prevent concurrent reuse.

Callback and webhook

callbackUri only returns the browser. It must be identical to the registered URI—no wildcard, prefix, or normalization—and is not proof of the outcome.

identity.verification.completed is delivered for APPROVED, FAILED, and EXPIRED, with X-Signature. Verify the raw bytes and deduplicate on the envelope id. See the complete example in Account webhooks.

Evidence

GET /v1/identity-verifications/{verificationId}/artifacts requires kyc.read instead of the management scope and the kyc_artifacts feature. Sign it like every other call on the client host. The response may contain biometrics and provider evidence; do not log URLs or contents.

Main errors

HTTPerrorCodeMeaning
400invalid_document, invalid_purpose, invalid_reference_idInvalid creation field
400invalid_display_message, invalid_verification_ttl, invalid_consume_ttlInvalid text or range
400invalid_callback_uriURI is not an exact registered match
403feature_disabledFeature is disabled
403insufficient_scopeMissing identity_verification.manage or kyc.read
403forbiddenCall did not use the master M2M credential
404verification_not_foundID does not exist in this tenant
409verification_not_consumableIncompatible state, window, or consumption binding
409reference_already_consumedAnother proof already authorized this purpose/reference
429identity_verification_monthly_limit_exceededTen 201 creations already used in the BRT month
429rate_limitedTraffic limit, separate from quota