> ## Documentation Index
> Fetch the complete documentation index at: https://docs.yuvexpay.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Error handling

> Understand API error codes and how to handle them.

The YuvexPay API uses standard HTTP status codes and returns structured error responses.

## Error response format

All errors follow a consistent structure:

```json theme={null}
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Invalid request body",
    "details": {
      "amount": "Must be greater than 0.01"
    }
  }
}
```

| Field           | Type   | Description                    |
| --------------- | ------ | ------------------------------ |
| `error.code`    | string | Machine-readable error code.   |
| `error.message` | string | Human-readable description.    |
| `error.details` | object | Additional context (optional). |

<Note>
  Most errors use the nested envelope above. A few low-level guards return a **flat** envelope instead — notably the **idempotency middleware** (`IDEMPOTENCY_KEY_REQUIRED`, `IDEMPOTENCY_PAYLOAD_MISMATCH`, `IDEMPOTENCY_CONFLICT`):

  ```json theme={null}
  {
    "statusCode": 400,
    "error": "Bad Request",
    "message": "x-idempotency-key header is required for this operation",
    "code": "IDEMPOTENCY_KEY_REQUIRED"
  }
  ```

  In the flat shape, `error` is a **string** (the HTTP reason phrase), not an object, and the machine-readable code lives in the top-level `code` field. To handle both shapes, read the code defensively: `body.error?.code ?? body.code`.
</Note>

## HTTP status codes

| Status | Meaning                                             |
| ------ | --------------------------------------------------- |
| `200`  | Success.                                            |
| `201`  | Resource created.                                   |
| `400`  | Bad request (validation error, missing fields).     |
| `401`  | Unauthorized (missing, invalid or revoked API key). |
| `403`  | Forbidden (insufficient permissions).               |
| `404`  | Resource not found.                                 |
| `409`  | Conflict (idempotency key mismatch).                |
| `429`  | Rate limit exceeded.                                |
| `500`  | Internal server error.                              |
| `503`  | Service unavailable.                                |

## Error codes

### Authentication errors

| Code               | Status | Description                                                                                                |
| ------------------ | ------ | ---------------------------------------------------------------------------------------------------------- |
| `API_KEY_REQUIRED` | 401    | No `Authorization: Bearer` header supplied.                                                                |
| `INVALID_API_KEY`  | 401    | The key is unknown, revoked, expired, or its environment does not match the request.                       |
| `FORBIDDEN`        | 403    | The key is valid but lacks the required scope, or the request came from an IP outside the key's allowlist. |
| `IP_NOT_ALLOWED`   | 403    | The request came from an IP address outside the key's IP allowlist.                                        |

### Validation errors

| Code               | Status | Description                                                                        |
| ------------------ | ------ | ---------------------------------------------------------------------------------- |
| `VALIDATION_ERROR` | 400    | Request body or query parameters failed validation. Check `details` for specifics. |

### Idempotency errors

| Code                           | Status | Description                                                      |
| ------------------------------ | ------ | ---------------------------------------------------------------- |
| `IDEMPOTENCY_KEY_REQUIRED`     | 400    | POST endpoints that require an `X-Idempotency-Key` header.       |
| `IDEMPOTENCY_PAYLOAD_MISMATCH` | 409    | The same idempotency key was used with a different request body. |
| `IDEMPOTENCY_CONFLICT`         | 409    | The request is still being processed from a previous attempt.    |

### Rate limiting errors

| Code                  | Status | Description                                                 |
| --------------------- | ------ | ----------------------------------------------------------- |
| `RATE_LIMIT_EXCEEDED` | 429    | Too many requests. Check response headers for reset timing. |

### Resource errors

| Code        | Status | Description                                                               |
| ----------- | ------ | ------------------------------------------------------------------------- |
| `NOT_FOUND` | 404    | The requested resource does not exist or does not belong to your account. |

### Payment errors

| Code                               | Status | Description                                                                                                            |
| ---------------------------------- | ------ | ---------------------------------------------------------------------------------------------------------------------- |
| `PAYMENT_CREATION_BLOCKED`         | 403    | The account is currently not allowed to create payments (KYB pending, suspended, or the requested method is disabled). |
| `AMOUNT_REQUIRED`                  | 400    | A direct amount is required when no `productId` is supplied.                                                           |
| `CUSTOMER_DATA_REQUIRED`           | 400    | The selected payment method requires customer data and none was provided.                                              |
| `CARD_HOLDER_INFO_REQUIRED`        | 400    | Direct card payments require `card.holderInfo` (postal code and address number).                                       |
| `CARD_REMOTE_IP_REQUIRED`          | 400    | Direct card payments require `card.remoteIp` (the payer's IP address).                                                 |
| `INSTALLMENTS_NOT_SUPPORTED`       | 400    | Card installments are not currently supported; send `installments: 1` or omit.                                         |
| `PROVIDER_TEMPORARILY_UNAVAILABLE` | 502    | A downstream payment processor is unavailable. Retry with backoff.                                                     |

### Refund errors

| Code                              | Status | Description                                                                                               |
| --------------------------------- | ------ | --------------------------------------------------------------------------------------------------------- |
| `INVALID_STATUS`                  | 400    | The payment is not in a refundable status.                                                                |
| `PARTIAL_REFUND_NOT_SUPPORTED`    | 400    | Only full refunds are currently supported. Send no `amount` or send the full remaining refundable amount. |
| `ALREADY_REFUNDED`                | 400    | The payment has already been fully refunded.                                                              |
| `REFUND_IN_PROGRESS`              | 409    | A refund is already being processed for this payment.                                                     |
| `REFUND_NOT_SUPPORTED`            | 400    | Refunds are not supported for this payment method or processing route.                                    |
| `INSUFFICIENT_BALANCE_FOR_REFUND` | 400    | Your available balance is not sufficient to issue this refund.                                            |

### Withdrawal errors

| Code                            | Status | Description                                                                                                                           |
| ------------------------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------- |
| `WITHDRAWAL_BELOW_MINIMUM`      | 400    | The amount is below the minimum withdrawal value.                                                                                     |
| `INSUFFICIENT_BALANCE`          | 400    | Your available balance is not sufficient.                                                                                             |
| `WITHDRAWAL_LIMIT_EXCEEDED`     | 400    | The amount exceeds the remaining daily or nightly limit.                                                                              |
| `WITHDRAWALS_BLOCKED`           | 403    | Withdrawals are currently disabled for the account.                                                                                   |
| `INVALID_PIX_KEY`               | 400    | The destination PIX key is not a recognizable CPF, CNPJ, e-mail, phone, or random key.                                                |
| `INVALID_WITHDRAWAL_RECIPIENT`  | 403    | The PIX key does not belong to a permitted recipient (for CNPJ accounts, the key must match the company name or registered partners). |
| `PIX_KEY_VALIDATION_FAILED`     | 400    | The PIX key could not be validated.                                                                                                   |
| `CREDITOR_DOCUMENT_UNAVAILABLE` | 403    | Your account's CNPJ (business) or owner CPF (individual) is not on file. Complete KYB/KYC before withdrawing.                         |
| `VERIFICATION_REQUIRED`         | 403    | Account verification is required before withdrawals can be processed.                                                                 |

### Server errors

| Code                  | Status | Description                                                        |
| --------------------- | ------ | ------------------------------------------------------------------ |
| `INTERNAL_ERROR`      | 500    | Unexpected server error. Retry with exponential backoff.           |
| `SERVICE_UNAVAILABLE` | 503    | The service is temporarily unavailable. Retry after a short delay. |

## Handling errors

```javascript theme={null}
async function createPayment(data) {
  const response = await fetch("https://api.yuvexpay.com/v1/payments", {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${process.env.YUVEX_API_KEY}`,
      "Content-Type": "application/json",
      "X-Idempotency-Key": generateIdempotencyKey(),
    },
    body: JSON.stringify(data),
  });

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

    switch (response.status) {
      case 400:
        console.error("Validation error:", error.details);
        break;
      case 401:
      case 403:
        throw new Error(`Auth failed: ${error.code} — ${error.message}`);
      case 409:
        console.warn("Duplicate request detected");
        break;
      case 429:
        const retryAfter = response.headers.get("X-RateLimit-Reset");
        await sleep(retryAfter * 1000);
        return createPayment(data);
      default:
        throw new Error(`API error: ${error.message}`);
    }
  }

  return response.json();
}
```
