Skip to content
Last updated

Errors from the ConnectPSP API return a standardized RFC 7807 Problem Details JSON object. Two authentication failures are the exception — see Authentication Errors.

Error Format

{
  "type": "https://doc.connectpsp.com/errors/duplicated-external-reference",
  "title": "Referência Externa Duplicada",
  "status": 409,
  "code": "DUPLICATED_EXTERNAL_REFERENCE",
  "detail": "Já existe uma operação com o externalReference informado: withdraw_xyz789."
}
FieldDescription
typeURI that identifies the error category.
titleShort, human-readable summary (stable across occurrences).
statusHTTP status code.
codeMachine-readable error code. Use this for programmatic handling.
detailHuman-readable explanation of this specific error occurrence.

Synchronous errors vs. cancelled Cash-Outs

Two different things can go wrong with a Cash-Out, and they reach you through different channels:

WhatHow you find out
The request itself is invalid (bad body, duplicated externalReference, reused idempotency key)An HTTP error on POST /cash-out, described on this page
The request was accepted (202) but could not be paid — balance, limits, PIX key, receiving institutionA CASHOUT_FAILED webhook with status: "CANCELLED" and a failure.code

POST /cash-out does not check your balance or your withdrawal limits before answering. Those checks run after the 202 Accepted, so an insufficient balance shows up as failure.code: "INSUFFICIENT_FUNDS" on a cancelled Cash-Out, never as an HTTP error. The full list of failure.code values, with their messages and whether to retry, is in the Webhooks guide.


HTTP Status Codes

StatusWhen it occurs
400 Bad RequestInvalid JSON, missing required field, field validation failed, or invalid X-Idempotency-Key.
401 UnauthorizedWrong credentials on POST /auth/token; missing, expired or invalid JWT; missing or invalid ApplicationToken; missing or invalid DigitalSignature.
403 ForbiddenNo CryptoToken configured for your client, so DigitalSignature cannot be checked.
404 Not FoundThe requested transaction does not exist for this client.
409 ConflictDuplicated externalReference on a Cash-In or Cash-Out, or an X-Idempotency-Key still being processed.
422 Unprocessable EntityBusiness rule violation (see codes below).
500 Internal Server ErrorUnexpected server error — retry with backoff, contact support if it persists.

Error Codes Reference

Authentication Errors

These errors have no code. Tell them apart by the endpoint, the body and the headers.

CaseStatusWhat you receiveWhat to do
Wrong clientId or clientSecret on POST /auth/token (also a clientSecret that is not a GUID)401{ "message": "As credenciais informadas não são válidas." } — a plain object, not Problem DetailsCheck both credentials. The message is the same whichever one is wrong.
clientId or clientSecret missing on POST /auth/token400Problem Details with code: "VALIDATION_ERROR"Send both fields.
JWT missing, invalid or expired, on any other endpoint401No body. The reason is in the WWW-Authenticate header (below)Request a new token with POST /auth/token and retry.
ApplicationToken header missing401Problem Details, title: "ApplicationToken Ausente"Send the header.
ApplicationToken not a GUID, or not found / inactive401Problem Details, title: "ApplicationToken Inválido"; detail says whichCheck the value issued by ConnectPSP.

The JWT is checked first: a request with an expired JWT and a wrong ApplicationToken gets the JWT 401. The WWW-Authenticate header of the JWT 401:

SituationWWW-Authenticate
No Authorization: Bearer headerBearer
Token expiredBearer error="invalid_token", error_description="The token expired at '<date>'"
Token rejected for another reason (bad signature, malformed)Bearer error="invalid_token", error_description="<reason>"

The ApplicationToken 401 body:

{
  "type": "https://doc.connectpsp.com/errors/unauthorized",
  "title": "ApplicationToken Inválido",
  "status": 401,
  "detail": "O ApplicationToken fornecido não foi encontrado ou está inativo."
}

Digital Signature Errors

Only on POST /cash-out and POST /cash-in/{id}/refund. The DigitalSignature is checked after the JWT and the ApplicationToken, and after the body is validated (an invalid body answers 400 VALIDATION_ERROR first). These errors carry a code.

CodeStatusDescription
DIGITAL_SIGNATURE_REQUIRED401The DigitalSignature header is missing.
DIGITAL_SIGNATURE_INVALID401The HMAC does not match your JWT and CryptoToken. See the Authentication Guide.
CRYPTO_TOKEN_NOT_CONFIGURED403Your client has no active CryptoToken, so the signature cannot be checked. Ask ConnectPSP support to issue one.

Request Errors

Common to the Cash-In and Cash-Out endpoints.

CodeStatusDescription
VALIDATION_ERROR400One or more request fields failed validation, or the JSON body is invalid. errors lists the messages per field.
DUPLICATED_EXTERNAL_REFERENCE409You already created a Cash-In (on POST /cash-in) or a Cash-Out (on POST /cash-out) with this externalReference — even one that was later cancelled — or one with it is being created right now.
INTERNAL_ERROR500Unexpected error on our side. Retry with backoff; if it persists, send the traceId to support.

Idempotency Errors

Only when you send the optional X-Idempotency-Key header on POST /cash-in or POST /cash-out. See Idempotency.

CodeStatusDescription
IDEMPOTENCY_KEY_INVALID400The key is longer than 100 characters.
IDEMPOTENCY_KEY_IN_PROGRESS409A request with this key is still being processed. Check the result in a moment.
IDEMPOTENCY_KEY_REUSED422The key was already used with a different body. Generate a new key for a new operation.

Cash-In Errors

CodeStatusDescription
CASHIN_NOT_FOUND404No Cash-In matches the {id} for this client (GET /cash-in/{id} and POST /cash-in/{id}/refund).
REFUND_NOT_ALLOWED422The Cash-In status is not PAID — not paid yet, expired, cancelled, or already refunded. detail says which.
MISSING_E2E422The payment endToEndId is not available yet, so the devolution cannot be sent. Retry later.

Cash-Out Errors

CodeStatusDescription
CASHOUT_NOT_FOUND404No Cash-Out matches the {id} for this client (GET /cash-out/{id}).
RULE_VIOLATION422Generic business rule violation from the payment flow. Check detail.

Why an accepted Cash-Out was cancelled is not an HTTP error: it comes as failure.code on the CASHOUT_FAILED webhook and on GET /cash-out/{id}. See the failure.code reference.

Account Errors

CodeStatusDescription
INSUFFICIENT_PROPRIETARY_FUNDS422Top-up failed — not enough funds in the Proprietary account.
INSUFFICIENT_TRANSACTIONAL_FUNDS422Sweep failed — not enough funds in the Transactional account.
RETROACTIVE_BALANCE_NOT_IMPLEMENTED422Historical balance lookup is not yet available.
INVALID_AMOUNT422Rebalance amount must be greater than zero.

PIX Health Errors

Read-only endpoints, so no business rules — only "no data" and "try again". Note that the code here is kebab-case for the invalid-parameter cases, unlike every other code in this page.

CodeStatusDescription
PIX_HEALTH_UNAVAILABLE503We could not read the picture or the incidents. Reflects an internal read failure, not the state of PIX. Honour Retry-After (60 s) and retry with backoff.
PIX_INCIDENT_NOT_FOUND404No incident with that id is visible to you — it does not exist, or it is outside your scope. The two cases are indistinguishable on purpose.
invalid-window422from is later than to, or the window exceeds 92 days.
invalid-status422A status value outside ACTIVE, WORSENING, RECOVERING, RESOLVED.
invalid-page422page below 1.
invalid-page-size422pageSize outside 1–200.
invalid-incident-id422The id in the path is blank or only whitespace.

GET /health/pix also answers 204 No Content when the engine has not produced any picture yet (a freshly deployed environment, for example). That is not an error — treat it as "no data yet" and retry later.


Handling Errors in Code

The POST /cash-out response

async function createCashOut(payload, idempotencyKey) {
  const response = await fetch('https://api.homologacao.connectpsp.com/cash-out', {
    method: 'POST',
    headers: {
      /* Authorization, ApplicationToken, DigitalSignature, Content-Type ... */
      'X-Idempotency-Key': idempotencyKey, // optional, recommended: reuse it only to retry this same request
    },
    body: JSON.stringify(payload)
  });

  if (response.status === 401 && response.headers.get('WWW-Authenticate')) {
    // JWT missing, invalid or expired: no body. Get a new token and retry.
    await refreshToken();
    return createCashOut(payload, idempotencyKey);
  }

  if (!response.ok) {
    const error = await response.json();

    switch (error.code) {
      case 'DUPLICATED_EXTERNAL_REFERENCE':
        // A Cash-Out with this externalReference already exists: GET /cash-out/{id}
        // accepts the externalReference, so fetch it and follow its status.
        return getCashOut(payload.externalReference);

      case 'IDEMPOTENCY_KEY_IN_PROGRESS':
        // The first attempt with this key is still running. Wait and check it.
        await sleep(2000);
        return getCashOut(payload.externalReference);

      case 'IDEMPOTENCY_KEY_REUSED':
        // Bug on our side: the same key was used for a different body.
        throw new Error('X-Idempotency-Key reused with a different payload.');

      case 'VALIDATION_ERROR':
        throw new Error(`Invalid Cash-Out request: ${error.detail}`);

      default:
        // Log and escalate unknown errors
        logger.error('ConnectPSP error', error);
        throw new Error(error.detail);
    }
  }

  // 202 Accepted: queued, NOT paid. The outcome arrives by webhook.
  return response.json();
}

The outcome, by webhook

function onCashOutEvent(event) {
  const { status, failure, reason } = event.data;

  switch (event.eventType) {
    case 'CASHOUT_COMPLETED':
      // status === 'COMPLETED'
      return markPaid(event.data);

    case 'CASHOUT_FAILED':
      // status === 'CANCELLED': the money never left your account
      switch (failure.code) {
        case 'INSUFFICIENT_FUNDS':
          return triggerRebalanceAlert(event.data);
        case 'PIX_KEY_NOT_FOUND':
        case 'PIX_KEY_INVALID':
          return askUserForAnotherKey(event.data);
        default:
          return markCancelled(event.data, failure.code, failure.message);
      }

    case 'CASHOUT_REFUNDED':
      // status === 'REFUNDED': paid, then returned to your account
      return markRefunded(event.data, reason);
  }
}