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

> ## Agent Instructions
> OwnPay is licensed under AGPL-3.0 and is completely free - no licensing fees.
> Production docs URL: https://ownpay.org/docs - append .md to any page URL for clean markdown.
> OwnPay requires PHP 8.3+, MySQL/MariaDB, and Redis.
> MCP server available at https://ownpay.org/docs/mcp for programmatic documentation queries.
> Use root-relative links (e.g. /quickstart) for internal navigation - do NOT include /docs prefix.
> Plugin development: consult /developer/plugin-types/ pages for correct interfaces and manifests.

# API Error Codes - HTTP Status Codes and Response Reference

> Reference for OwnPay API HTTP status codes, error response format, and common error codes covering payments, authentication, and validation.

OwnPay API returns consistent error responses across all endpoints.

## Error response format

```json theme={null}
{
  "success": false,
  "error": {
    "code": "invalid_amount",
    "message": "Amount must be greater than 0"
  }
}
```

## HTTP status codes

| Code | Meaning                                     |
| ---- | ------------------------------------------- |
| 200  | Success                                     |
| 201  | Created                                     |
| 400  | Bad request (validation error)              |
| 401  | Unauthorized (invalid API key)              |
| 403  | Forbidden (insufficient permissions)        |
| 404  | Resource not found                          |
| 409  | Conflict (duplicate resource)               |
| 422  | Unprocessable entity (business logic error) |
| 429  | Rate limit exceeded                         |
| 500  | Internal server error                       |

## Common error codes

### Authentication errors

| Code                       | Description                     |
| -------------------------- | ------------------------------- |
| `invalid_api_key`          | API key not found or malformed  |
| `expired_api_key`          | API key has expired             |
| `revoked_api_key`          | API key has been revoked        |
| `insufficient_permissions` | Key lacks required access level |

### Payment errors

| Code                  | Description                             |
| --------------------- | --------------------------------------- |
| `invalid_amount`      | Amount must be greater than 0           |
| `invalid_currency`    | Unsupported currency code               |
| `gateway_unavailable` | Payment gateway is down                 |
| `payment_declined`    | Gateway declined the payment            |
| `insufficient_funds`  | Customer account has insufficient funds |
| `expired_card`        | Card has expired                        |
| `invalid_card`        | Card details are invalid                |

### Resource errors

| Code             | Description                                        |
| ---------------- | -------------------------------------------------- |
| `not_found`      | Resource does not exist                            |
| `already_exists` | Resource already exists                            |
| `invalid_state`  | Resource is in an invalid state for this operation |

### Webhook errors

| Code                     | Description                           |
| ------------------------ | ------------------------------------- |
| `invalid_webhook_url`    | URL is not valid or not HTTPS         |
| `webhook_secret_missing` | Webhook secret not configured         |
| `signature_mismatch`     | Webhook signature verification failed |

## Handling errors

### Best practices

1. Check the `success` field first
2. Use the `error.code` for programmatic handling
3. Display `error.message` to users (it's human-readable)
4. Implement retry logic for 5xx errors
5. Log all errors for debugging

### Example error handling

```php theme={null}
$response = $client->payments()->create([...]);

if (!$response['success']) {
    $code = $response['error']['code'];
    $message = $response['error']['message'];
    
    match ($code) {
        'invalid_amount' => handleInvalidAmount(),
        'gateway_unavailable' => retryLater(),
        default => logError($code, $message),
    };
}
```


## Related topics

- [Dispatch Test Webhook](/docs/api-reference/dispatch-test-webhook.md)
- [Node.js SDK - TypeScript-First Payment Integration](/docs/developer/integration/nodejs.md)
- [Initiate Payment Intent](/docs/api-reference/initiate-payment-intent.md)
- [Retrieve Device Connection Status](/docs/api-reference/retrieve-device-connection-status.md)
- [List API Keys](/docs/api-reference/list-api-keys.md)
