# Errors

ZevPay uses standard HTTP status codes and consistent error response formats.

## Error response format

All error responses follow this structure:

```json
{
  "success": false,
  "error": {
    "code": "HTTP_ERROR",
    "message": "A human-readable error description"
  },
  "correlationId": "-"
}
```

| Field | Type | Description |
|-------|------|-------------|
| `success` | boolean | Always `false` for errors |
| `error.code` | string | Machine-readable error code |
| `error.message` | string | Human-readable description of what went wrong |
| `correlationId` | string | Request correlation ID (from `x-correlation-id` header, or `"-"`) |

## Error codes

| Code | Description |
|------|-------------|
| `HTTP_ERROR` | Standard HTTP error (bad request, not found, unauthorized, etc.) |
| `INTERNAL_ERROR` | Unexpected server error |

## HTTP status codes

| Code | Meaning | Description |
|------|---------|-------------|
| `200` | OK | Request succeeded |
| `400` | Bad Request | Invalid parameters, expired session, or business logic error |
| `401` | Unauthorized | Missing or invalid API key |
| `403` | Forbidden | Origin not allowed for this API key |
| `404` | Not Found | Resource does not exist |
| `429` | Too Many Requests | Rate limit exceeded |
| `500` | Internal Server Error | Something went wrong on our end |

## Common errors

### Authentication errors

| Status | Message | Cause | Solution |
|--------|---------|-------|----------|
| `401` | Invalid API key | API key not found or inactive | Check your API key is correct and active |
| `403` | Origin not allowed for this API key | Request origin doesn't match allowed domains | Add your domain to the API key's allowed domains in the dashboard |

### Session errors

| Status | Message | Cause | Solution |
|--------|---------|-------|----------|
| `404` | Session not found. | Invalid session ID | Verify the session ID is correct |
| `400` | Session is expired. | Session status is `expired` | Create a new session |
| `400` | Session is completed. | Payment already received | No action needed — payment is done |
| `400` | Session has expired. | Session's `expiresAt` time has passed | Create a new session |
| `400` | Session does not belong to this API key. | API key not from the same key pair | Use a key from the same key pair used to create the session |

### Payment method errors

| Status | Message | Cause | Solution |
|--------|---------|-------|----------|
| `400` | Unsupported payment method: {method} | Invalid `payment_method` value | Use `"bank_transfer"` or `"payid"` |

### Validation errors

| Status | Message | Cause | Solution |
|--------|---------|-------|----------|
| `400` | amount must not be less than 100 | Amount below minimum | Minimum is 100 kobo (NGN 1.00) |
| `400` | email must be an email | Invalid email format | Provide a valid email address |

### Rate limit errors

| Status | Message | Cause | Solution |
|--------|---------|-------|----------|
| `429` | Too Many Requests | Exceeded 100 requests/min (or 30/min for session initialization) | Wait and retry with exponential backoff — see [Rate Limiting](/guide/rate-limiting) |

::: tip Handling errors
Always check the `success` field in the response. If `false`, read `error.message` for details. Implement exponential backoff for `429` and `5xx` errors.
:::

## Error handling example

::: code-group

```js [Node.js]
const response = await fetch('https://api.zevpaycheckout.com/v1/checkout/session/initialize', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'x-api-key': 'sk_test_your_secret_key',
  },
  body: JSON.stringify({
    amount: 500000,
    currency: 'NGN',
    email: 'customer@example.com',
  }),
});

const result = await response.json();

if (!response.ok || result.success === false) {
  console.error(`Error: ${result.error?.message || 'Unknown error'}`);
  // Handle error appropriately
  return;
}

// Success
const session = result.data;
```

```python [Python]
import requests

response = requests.post(
    "https://api.zevpaycheckout.com/v1/checkout/session/initialize",
    headers={
        "Content-Type": "application/json",
        "x-api-key": "sk_test_your_secret_key",
    },
    json={
        "amount": 500000,
        "currency": "NGN",
        "email": "customer@example.com",
    },
)

result = response.json()

if not response.ok or not result.get("success"):
    error = result.get("error", {})
    print(f"Error: {error.get('message', 'Unknown error')}")
    # Handle error
else:
    session = result["data"]
```

:::
