Skip to main content

Error Handling

All API error responses follow a standard JSON format to facilitate programmatic handling.

Error Format

{
"errorCode": "missing_tenant",
"message": "X-Tenant-Id header is required"
}

The stable contract is errorCode — branch on it. message is presentation text (in Brazilian Portuguese) and may be rewritten without notice.

When the error originates at the banking provider, the response also carries a partner block with its raw code and message, so you can diagnose without opening a ticket:

{
"errorCode": "limit_exceeded_daily",
"message": "O valor excede o limite diurno desta conta. Limite restante: R$ 0,00.",
"partner": {
"code": "request.query.daily_limit_exceeded",
"message": "O valor R$ 1.000,00 excede seu limite diurno de pagamentos por Pix. Limite restante: R$ 0,00"
}
}

The partner block is informational: use it in logs and support tickets, never as a decision key in your code. It may carry code, message and field (the field the provider pointed at), and is omitted when the provider gives no detail.

General Error Catalog

CodeHTTPDescription
missing_tenant400The X-Tenant-Id header is required and was not provided.
tenant_mismatch403X-Tenant-Id does not match the tenant of the account in the path (or the body's tenantId).
tenant_forbidden403The token is not authorized for the tenant in X-Tenant-Id. Applies to every /v1/ route: credential and header must point to the same tenant.
tenant_suspended403There is an open pendency and write operations are suspended. Reads keep working. Contact CorpX support to clear it.
tenant_disabled403The tenant is disabled: no API route responds. Contact CorpX support to restore access.
account_not_found404The accountId in the path does not exist.
not_found404The requested resource does not exist.
invalid_payload400The request body (JSON) is invalid or contains validation errors.
invalid_body400The body is not valid JSON.
missing_fields / missing_field400Required fields are missing. The message names them.
invalid_field400A field value is outside the accepted format or limit. The message names the field and the limit.
idempotency_conflict409On POST /v1/accreditations/biometry-evidence, the evidenceId derived from the Idempotency-Key already belongs to another tenant. Reusing an Idempotency-Key with a different body does not return a conflict on the other endpoints: you get the result of the original operation. See Idempotency.
invalid_state409The operation does not apply to the resource's current state.
forbidden403You do not have permission to perform this action on the requested resource.
unauthorized401Missing Authorization header. An invalid or expired token returns 403 (obtain a new token via client_credentials).
insufficient_scope403The credential lacks the scope required by the route (e.g. qrcode.manage, pix_out.create), or is read-only attempting a write. The message names the missing scope. See the Authentication Guide.
user_token_forbidden_on_financial_op403User tokens cannot move money: financial operations require an application credential (client_credentials).
db_unavailable / banking_unavailable / temporal_unavailable503An internal dependency is temporarily unavailable. Safe to retry.
db_error500Internal persistence error.
internal_error500An unexpected error occurred on our servers.
workflow_start_failed / workflow_signal_failed502Failed to start or signal the internal workflow; retry with the same Idempotency-Key.

Banking Provider Errors

This is the complete list of errorCode values that represent a refusal or failure at the banking provider, as opposed to CorpX validation. It is generated from the catalog in our own code, so it will not drift from what the API actually emits.

Two things worth knowing:

  • The HTTP status is ours, not the provider's. The provider answers 403 for an exceeded limit, which many clients read as an authentication failure; we answer 422. Likewise, 410 Gone for a removed QR Code becomes 409, and any provider 5xx becomes 502.
  • Retryable below means repeating the same request (with the same Idempotency-Key) may yield a different outcome. Where it says "no", fix the data or the condition before resending.

The same codes appear in failure webhooks under data.errorCode, with the matching data.partner block.

CodeHTTPWhat happened and what to doRetryable
account_not_entitled409The account is fully accredited, but the banking provider has not enabled this service for it — other operations on the same account keep working. This is neither a transient failure nor a problem with your request: retrying will not help until the provider enables it. Contact support with the accountId.no
account_not_ready409The account has no counterpart at the banking provider yet — accreditation is in progress, was refused, or never completed. This is not an API outage: no banking operation works on this account until the accreditation reaches ACTIVE.no
already_at_partner409The document is already accredited at the banking provider by another participant. Requires a control transfer through support.no
bacen_rejected_content400Text content (description/message) contains a character the Brazilian Central Bank does not accept.no
balance_reservation_failed422Balance reservation failed (funds locked, settling, or racing with another operation).no
boleto_already_settled409The bank slip is already settled. Check the original payment before resending.no
boleto_amount_mismatch422Amount differs from the registered bank slip amount (consider interest, fine and discount).no
boleto_scheduled409There is an active schedule for the same barcode.no
challenge_failure422PIN/2FA validation failed at the banking provider.no
conflict409State conflict at the banking provider (e.g. resource already processed).no
identifier_conflict409The Identifier was already used at the banking provider. Resending the same value does not create a new operation.no
insufficient_funds422Insufficient balance as computed by the banking provider at settlement time.no
invalid_field400Field-level validation at the banking provider. partner.field names the field and the message carries the limit when available.no
invalid_payload400The banking provider rejected the request body. The partner block carries the original validation message.no
invalid_pix_key400Key format does not match the declared type (CPF, CNPJ, EMAIL, PHONE, EVP).no
key_inactive422The key exists but is inactive or under portability. Use another key or bank details.no
key_not_found404Key not found in DICT. Check for typos or ask the recipient for another key.no
key_ownership_mismatch422Attempt to register or use a key whose document differs from the account holder's.no
limit_exceeded422A limit was exceeded but the provider did not say which window.no
limit_exceeded_daily422Daytime window limit (06:00–20:00). The message carries the remaining limit when the provider reports it.no
limit_exceeded_monthly422Accumulated monthly limit. Only clears at month rollover or with a limit increase.no
limit_exceeded_nightly422Nighttime window limit (20:00–06:00), usually lower than the daytime one.no
limit_exceeded_transaction422Per-transaction limit. Split the operation or request a limit increase.no
not_found404Resource does not exist at the banking provider (id, key or transaction).no
partial_refund_not_supported400Legacy — no longer returned: partial refunds are now accepted. An amount above the original returns refund_amount_exceeded.no
partner_account_disabled422Source account is disabled at the banking provider (reactivation required).no
partner_error502Unclassified failure while talking to the banking provider. Safe to retry.yes
partner_forbidden502Operation blocked by the banking provider for this account (product or permission not enabled).no
partner_internal502Internal (5xx) error at the banking provider. Safe to retry.yes
partner_rate_limited429The banking provider throttled CorpX's traffic. Nothing is wrong with your request — slow down and retry with backoff.yes
partner_rejected422Refused by the banking provider on internal policy, risk or fraud grounds. The partner block carries the stated reason.no
partner_unauthorized502CorpX's own credential at the banking provider was rejected. Not related to your credential; wait a few minutes before resending.no
partner_unavailable502Provider outage or gateway failure. Safe to retry.yes
pix_key_limit_exceeded422Maximum number of keys per account reached (5 for individuals, 20 for companies).no
qr_amount_mismatch422A fixed-amount QR Code does not accept a different amount.no
qr_gone409QR Code removed or expired at the banking provider.no
recipient_account_not_found422Destination bank/branch/account does not exist. Applies to TED and internal transfers.no
refund_amount_exceeded400Refund amount is larger than the refundable balance of the original PIX.no
timeout504Timeout calling the banking provider. The operation may exist — query it before resending.yes

Business-Specific Errors

PIX Out (Transfers)

CodeHTTPDescription
policy_denied422A policy rule configured for the account or tenant refused the operation — transfer, QR code creation, PIX key creation, or refund. The body carries violations with the rule and message of each violated rule. See Policies and Rules.
invalid_identifier400identifier exceeds the accepted length or uses disallowed characters.
invalid_bank_code400On PIX by bank-account details, bankCode accepts a 3-digit Compe code or an 8-digit ISPB. TED accepts only the Compe code — see TED.
invalid_field400A known provider limit caught before sending — description over 140 characters, or branch/accountBranch over 4 digits.
dict_lookup_limit_exceeded429DICT lookup limits were exceeded: either an absolute count (maxLookupsPerDay, maxLookupsPerMinute, maxNotFoundPer5min) or a rate over a time window (lookups per transfer, or the fraction of lookups resolving no key, from 5 min to 30 days). Both the tenant/account policy and, for fields left blank, the global default apply — always counted per account, never summed across the tenant. The message indicates which counter or window triggered. Wait for the window to reset (00:00 BRT for per-day; sliding for the rates) or adjust the limits in the Backoffice. The ratio reason (maxLookupRatio) was discontinued in v2.53.0 and no longer appears.
beneficiary_not_found404The given beneficiary does not exist in the account's address book.
beneficiary_incomplete422The stored beneficiary lacks data required by the chosen operation.
beneficiary_not_active_at_partner422The beneficiary exists locally but is not active at the provider yet.
batch_too_large400BigPix: the requested amount would produce more than 99 chunks.
batch_not_found404BigPix: the requested batchId does not exist.

Balance, limits, PIX key and provider refusals use the codes in Banking Provider Errors.

QR Code

CodeHTTPDescription
missing_field400pixKey missing — and, on dynamic QR, value missing or not positive.
invalid_field400message over 140 characters, a provider limit.
invalid_identifier400identifier exceeds the accepted length or uses disallowed characters.
invalid_payload400expirationDate is not RFC 3339 or is in the past.
missing_param400Lookup and cancellation require the identifier query param (alias txid).
policy_denied422A policy rule refused the creation (qrCode.canCreateStatic, qrCode.canCreateDynamic, qrCode.minAmount, qrCode.maxAmount).

PIX Keys

CodeHTTPDescription
invalid_body400The body is not valid JSON.
missing_field400keyType missing, or pixKey missing for any type other than random.
invalid_pix_key400The key in the path could not be decoded (remember to URL-encode email and phone keys).
policy_denied422A policy rule refused the creation (keys.canCreate, keys.maxKeys).

The key format itself is validated by the provider and comes back as invalid_pix_key; their per-account key limit comes back as pix_key_limit_exceeded.

Refunds (PIX Refund)

CodeHTTPDescription
invalid_payload400reason missing or outside the official refund reason list.
original_transaction_not_found404The original transaction (originalEndToEnd) was not found.

refund_amount_exceeded (amount above the original, or above the balance still refundable) comes from the provider — see the previous section. partial_refund_not_supported is no longer returned: partial refunds are now accepted.

API-initiated refunds also go through the policy: policy_denied (422) when the Refund section is switched off (refund.disabled) or the amount exceeds refund.maxAmount.

TED

CodeHTTPDescription
invalid_bank_code400bankCode must be the 3-digit Compe code. TED does not accept ISPB — the provider contract has no such field, and sending it would leave the TED hanging.
invalid_account_type400accountType must be CHECKING, SAVINGS, PAYMENT or SALARY. Omitted, it defaults to CHECKING.
invalid_tax_number400taxNumber must have 11 (CPF) or 14 (CNPJ) digits.
invalid_identifier400identifier exceeds the accepted length or uses disallowed characters.
invalid_field400description over 140 characters, or branch over 4 digits (send the branch without the check digit).
missing_fields400Missing value, holderName, branch, or account.

TED deduplicates by identifier and never re-executes — resending an identifier already used returns 200 with the current state, including a failed one. See Idempotency.

Internal Transfers

CodeHTTPDescription
multiple_destination_accounts409On /transfers/internal/by-document, the document has more than one active account. Address it by account instead: /transfers/internal/by-bank-account (branch + number) or /transfers/internal (destinationAccountId).

A provider refusal comes back as 422 with status: "FAILED" and a code from Banking Provider Errors — usually insufficient_funds or partner_rejected.

Statement, Entries, and Exports

CodeHTTPDescription
invalid_date_range400startDate / endDate outside the YYYY-MM-DD format.
date_range_too_wide400The statement window exceeds 31 days. Split it into smaller periods.
invalid_operation400The entry detail requires the operation query param: PIX, TED, BOLETO, INTERNAL, or FEE.
invalid_query400The per-account timeline requires exactly one of endToEndId and identifier — never both, never neither.
invalid_format400Export format must be csv or pdf.
not_ready409Download requested before the export finished. Poll the status until completed.
presign_failed500Failed to generate the download link for an already finished file. Safe to retry.

Webhooks (subscriptions)

CodeHTTPDescription
missing_field400url missing when creating the subscription.
invalid_url400url must use https://.
hookdeck_sync_failed502Failed to register the destination at our webhook delivery service. Safe to retry.

Accreditation / Account Opening

Errors from POST /v1/accreditations/pf|pj, POST /v1/accreditations/biometry-evidence, POST /v1/accreditations/{id}/persons/{cpf}/retry and queries.

CodeHTTPDescription
feature_disabled403The accreditation (BaaS) feature is not enabled for your tenant. Contact CorpX to enable it. (Reads remain available.)
missing_fields400Required fields missing (e.g. documentType, document, legalName; PF: person.cpf; PJ: company.cnpj and at least one partner).
invalid_document_type400Invalid documentType — use CPF or CNPJ.
invalid_document400Document has an unexpected digit count (CPF=11, CNPJ=14).
invalid_field400A provider limit caught at POST time: address.number over 10 characters (move the rest to address.complement) or address.cityIbgeCode without 7 digits / inconsistent with address.state. An address.complement shorter than 3 characters is not an error: the field is optional at the provider and is omitted from the submission.
mixed_biometry_mode422PJ: all partners must use the same biometry mode (all unico or all BYO), no mixing.
unsupported_provider422Unrecognized BYO biometry provider. Accepted: unico, serpro, idwall, sumsub, clearsale, caf, valid.
provider_not_enabled422The given BYO provider is not enabled for your tenant.
evidence_not_found422The biometry.evidenceId does not exist (or does not belong to your tenant). Upload it via POST /v1/accreditations/biometry-evidence first.
not_pj422Company document upload is only accepted on PJ accreditations (POST .../documents).
invalid_kind422Invalid company document kind. Accepted: company_articles, company_proof_of_address, cnpj_card, financial_statements, business_license, regulatory_license, other.
invalid_content_type422contentType must be application/pdf for company document uploads.
invalid_state409Operation not applicable to the current state (e.g. retry of an already-approved biometry, or accreditation in a terminal state).
invalid_status409Company document upload blocked for the current status (only PENDING_BIOMETRY / BIOMETRY_APPROVED / PENDING_REVIEW).
not_found404Accreditation (or a person within it) not found.
rate_limited429Too many authorization requests for CPFs that already have an account in the last hour. Retry later; sustained bursts are reviewed by CorpX.
presign_error500Failed to generate the upload link for the evidence or corporate document. Safe to retry.
storage_unavailable503KYC archive (evidence bucket) temporarily unavailable.
workflow_signal_failed502Failed to signal the accreditation workflow; retry.

CorpX validation errors on the same endpoints, with text generated from the code's own catalog:

CodeHTTPWhat happened and what to doRetryable
invalid_callback_uri400The callbackUri you sent is not registered for your tenant, or is not accepted (only https:// and an application scheme; no credentials in the URI, no #, no localhost or internal network address). The URI must be registered by CorpX support before its first use, and the comparison is exact. See Returning to your app.no

A provider refusal of the accreditation arrives asynchronously, in the accreditation.failed webhook and in GET /v1/accreditations/{id}, under reason:

ReasonWhat happened
partner_validation_failedThe provider rejected a registration field. The message names the field and the partner block carries the original validation.
existing_partner_accountThe document already has an account at the provider — a control transfer through support is required.
partner_unavailableProvider outage. Transient: resend the accreditation.
partner_rejectedRefusal on provider policy/risk grounds, with no specific field.

Common Error Examples

Insufficient Balance

{
"errorCode": "insufficient_funds",
"message": "Saldo insuficiente na conta do liquidante para concluir a operação.",
"partner": {
"code": "request.client.insufficient_balance",
"message": "Saldo insuficiente para realizar a operação"
}
}

Field Over the Provider's Limit

{
"errorCode": "invalid_field",
"message": "O campo \"Identifier\" não é aceito pelo liquidante com o valor enviado. O valor deve ter no máximo 50 caracteres.",
"partner": {
"code": "request.value.above_maximum",
"field": "Identifier",
"message": "O valor 'Identifier' deve ter no máximo 50 caracteres."
}
}

Idempotency Conflict

{
"errorCode": "idempotency_conflict",
"message": "Request with same Idempotency-Key but different payload"
}

Insufficient Scope

{
"errorCode": "insufficient_scope",
"message": "esta credencial não tem escopo para esta operação; é necessário o escopo qrcode.manage"
}

Create a credential with the required scope in the backoffice panel (API Credentials). Money-moving scopes are issued by CorpX.

Monitoring and Support

If you receive frequent 5xx errors (internal_error, partner_error, partner_unavailable, partner_internal), we recommend:

  1. Implement exponential retry with the same idempotency key (maximum 3 attempts), only for codes marked as retryable.
  2. Check the status of our platform in case of prolonged unavailability.
  3. Contact support providing:
    • The received errorCode, plus the partner block when present
    • The sent payload (without sensitive data)
    • The X-Request-Id from the response (if available)
    • Approximate timestamp of the error

Support Channels

  • Slack: Private channel per client — request access from the CorpX team during onboarding.
  • Email: api@corpx.com