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

# Error Codes

> API error response details

## Error Response Format

API errors are returned in the following format.

```json theme={null}
{
  "error": {
    "type": "invalid_request_error",
    "code": "validation_error",
    "message": "Error description",
    "request_id": "req_abc123xyz789"
  }
}
```

<ResponseField name="type" type="string">
  Error type (`authentication_error`, `invalid_request_error`, `payment_error`, `api_error`)
</ResponseField>

<ResponseField name="code" type="string">
  Error code (see below)
</ResponseField>

<ResponseField name="message" type="string">
  Error description
</ResponseField>

<ResponseField name="request_id" type="string">
  Request ID (use when contacting support)
</ResponseField>

<ResponseField name="param" type="string">
  Parameter that caused the error (for validation errors)
</ResponseField>

<ResponseField name="details" type="array">
  Detailed error information (for validation errors)
</ResponseField>

## Authentication Errors

| Code                  | HTTP Status | Description                                |
| --------------------- | ----------- | ------------------------------------------ |
| `unauthorized`        | 401         | Authorization header is invalid or missing |
| `merchant_not_active` | 403         | Merchant account is deactivated            |

## Validation Errors

| Code                   | HTTP Status | Description                                                    |
| ---------------------- | ----------- | -------------------------------------------------------------- |
| `validation_error`     | 400         | Invalid request parameters. Details are in the `details` field |
| `invalid_amount`       | 400         | Invalid amount                                                 |
| `amount_below_minimum` | 400         | Amount is below minimum                                        |
| `amount_above_maximum` | 400         | Amount exceeds maximum                                         |
| `unsupported_currency` | 400         | Unsupported currency                                           |

## Payment Errors

| Code                      | HTTP Status | Description                                     |
| ------------------------- | ----------- | ----------------------------------------------- |
| `payment_failed`          | 400         | Payment processing failed                       |
| `invalid_status`          | 400         | Operation cannot be performed in current status |
| `invalid_state`           | 400         | Invalid transaction state                       |
| `invalid_transaction`     | 400         | Transaction is invalid                          |
| `not_supported`           | 400         | This operation is not supported                 |
| `capture_failed`          | 400         | Capture failed                                  |
| `refund_failed`           | 400         | Refund failed                                   |
| `card_limit_exceeded`     | 400         | Monthly card spending limit exceeded            |
| `email_limit_exceeded`    | 400         | Monthly email spending limit exceeded           |
| `recurring_not_supported` | 400         | Connector does not support recurring payments   |

## Token Errors

| Code                         | HTTP Status | Description                                              |
| ---------------------------- | ----------- | -------------------------------------------------------- |
| `tokenization_failed`        | 400         | Failed to tokenize card data                             |
| `payment_token_expired`      | 400         | Token has expired. Create a new token. (30 minute limit) |
| `payment_token_already_used` | 400         | Token has already been used for a payment                |

## Webhook Payment Error Codes

When a payment fails, the `payment.failed` webhook includes detailed error information in a unified format that is consistent across all payment providers.

### Error Structure

```json theme={null}
{
  "event": "payment.failed",
  "error": {
    "code": "card_declined",
    "category": "soft_decline",
    "message": "Your card was declined",
    "recommended_action": "use_different_card"
  }
}
```

### Error Categories

| Category         | Description                                       | Recommended Action                           |
| ---------------- | ------------------------------------------------- | -------------------------------------------- |
| `authentication` | 3DS authentication required                       | Redirect customer to complete authentication |
| `soft_decline`   | Temporary failure                                 | Retry or use a different card                |
| `hard_decline`   | Permanent failure                                 | Do not retry with the same card              |
| `gateway_error`  | Payment gateway issue                             | Retry later                                  |
| `validation`     | API request validation failure (no webhook fired) | Fix the request and retry                    |

### Unified Error Codes

| Code                      | Category       | Description                                                 | Recommended Action   |
| ------------------------- | -------------- | ----------------------------------------------------------- | -------------------- |
| `authentication_required` | authentication | 3DS authentication required                                 | `contact_customer`   |
| `authentication_failed`   | soft\_decline  | 3DS authentication failed                                   | `retry`              |
| `authentication_timeout`  | gateway\_error | 3DS authentication timed out                                | `retry`              |
| `card_declined`           | soft\_decline  | Card was declined                                           | `use_different_card` |
| `insufficient_funds`      | soft\_decline  | Insufficient funds                                          | `use_different_card` |
| `expired_card`            | hard\_decline  | Card has expired                                            | `use_different_card` |
| `invalid_card`            | hard\_decline  | Invalid card number                                         | `use_different_card` |
| `invalid_cvc`             | soft\_decline  | Invalid security code                                       | `retry`              |
| `invalid_expiry`          | soft\_decline  | Invalid expiration date                                     | `retry`              |
| `fraud_detected`          | hard\_decline  | Suspected fraud                                             | `none`               |
| `stolen_card`             | hard\_decline  | Stolen card                                                 | `none`               |
| `lost_card`               | hard\_decline  | Lost card                                                   | `none`               |
| `processing_error`        | gateway\_error | Processing error                                            | `retry`              |
| `gateway_timeout`         | gateway\_error | Gateway timeout                                             | `retry`              |
| `gateway_unavailable`     | gateway\_error | Gateway unavailable                                         | `retry`              |
| `payment_failed`          | soft\_decline  | Payment failed (generic)                                    | `use_different_card` |
| `three_ds_required`       | authentication | 3DS authentication required but not supported for this card | `use_different_card` |
| `refund_failed`           | gateway\_error | Refund operation failed                                     | `contact_customer`   |
| `void_failed`             | gateway\_error | Void operation failed                                       | `contact_customer`   |
| `status_check_failed`     | gateway\_error | Unable to check payment status                              | `retry`              |
| `unknown_error`           | soft\_decline  | Unknown error                                               | `use_different_card` |

**Validation errors** (API request failures — visible in 400 response, **no webhook fired**):

| Code                            | Description                                    |
| ------------------------------- | ---------------------------------------------- |
| `unsupported_currency`          | Currency not supported by this payment method  |
| `invalid_amount`                | Amount is not a valid positive integer         |
| `invalid_parameter_combination` | Request parameters have an invalid combination |
| `payment_method_not_found`      | Saved payment method not found                 |
| `customer_not_found`            | Customer not found                             |
| `missing_card`                  | Card data is required but missing              |
| `not_supported`                 | Operation not supported by this connector      |

### Handling Example

```javascript theme={null}
// Webhook handler
app.post('/webhooks/zafapay', (req, res) => {
  const { event, error } = req.body;

  if (event === 'payment.failed' && error) {
    switch (error.category) {
      case 'authentication':
        // Send email to customer with authentication link
        notifyCustomerForAuth(error.message);
        break;
      case 'soft_decline':
        // Can retry or ask for different card
        if (error.recommended_action === 'retry') {
          scheduleRetry();
        } else {
          requestNewCard();
        }
        break;
      case 'hard_decline':
        // Do not retry, notify customer
        notifyCustomerCardInvalid(error.message);
        break;
      case 'gateway_error':
        // Temporary issue, schedule retry
        scheduleRetryWithBackoff();
        break;
    }
  }

  res.json({ received: true });
});
```

<Note>
  Error details from the original payment provider (PSP-specific codes) are not exposed in API responses or webhooks. Use the unified `code` and `category` fields to determine the appropriate action.
</Note>

## Refund Errors

| Code             | HTTP Status | Description                                                 |
| ---------------- | ----------- | ----------------------------------------------------------- |
| `invalid_amount` | 400         | Refund amount exceeds payment amount                        |
| `invalid_status` | 400         | Not in refundable status (only `completed` can be refunded) |

## Resource Errors

| Code                         | HTTP Status | Description                         |
| ---------------------------- | ----------- | ----------------------------------- |
| `not_found`                  | 404         | Specified resource not found        |
| `flow_not_found`             | 404         | Payment flow not found              |
| `no_default_flow`            | 404         | Default payment flow not configured |
| `flow_not_active`            | 400         | Payment flow is disabled            |
| `connector_config_not_found` | 404         | Connector configuration not found   |

## Idempotency Errors

| Code                       | HTTP Status | Description                                      |
| -------------------------- | ----------- | ------------------------------------------------ |
| `idempotency_key_mismatch` | 400         | Different request sent with same idempotency key |
| `idempotency_key_in_use`   | 409         | Idempotency key is currently being processed     |

## System Errors

| Code             | HTTP Status | Description                                     |
| ---------------- | ----------- | ----------------------------------------------- |
| `internal_error` | 500         | Internal error occurred. Please contact support |

## Error Handling Best Practices

<Steps>
  <Step title="Check HTTP Status Code">
    * `4xx`: Client error (request needs modification)
    * `5xx`: Server error (retriable)
  </Step>

  <Step title="Branch Logic by Error Code">
    Use `error.code` to implement error-type-specific handling
  </Step>

  <Step title="Retry Strategy">
    For `5xx` errors or `idempotency_key_in_use`, retry with exponential backoff
  </Step>
</Steps>

## Sample Code

```javascript Node.js theme={null}
async function createPayment(data) {
  const response = await fetch('https://api.sandbox.zafapay.com/v1/payments', {
    method: 'POST',
    headers: {
      'Authorization': 'Bearer YOUR_ACCESS_TOKEN',
      'Content-Type': 'application/json',
      'Idempotency-Key': generateIdempotencyKey()
    },
    body: JSON.stringify(data)
  });

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

    // Log request_id for support inquiries
    console.error(`API Error [${error.request_id}]:`, error.code, error.message);

    switch (error.type) {
      case 'authentication_error':
        // Token refresh required
        throw new AuthError(error.message);

      case 'invalid_request_error':
        // Check request parameters
        if (error.details) {
          console.error('Validation errors:', error.details);
        }
        throw new ValidationError(error.message);

      case 'payment_error':
        // Payment failed (try another payment method)
        throw new PaymentError(error.message);

      case 'api_error':
        // Retriable
        throw new RetryableError(error.message);

      default:
        throw new Error(error.message);
    }
  }

  return response.json();
}
```
