Error Reference
HTTP status codes, error response format, and per-endpoint error details.
Error Response Format
All error responses follow a consistent JSON structure:
{
"detail": "Human-readable error message",
"status_code": 400
}
The status_code field is always present in the response body, matching the HTTP status code. This is enforced by a custom exception handler.
Validation Error Format
Field-level validation errors return a nested object under detail:
{
"detail": {
"gateway_id": "gateway_id is required when campaign_id is not provided.",
"amount": "amount is required when campaign_id is not provided.",
"currency": "currency is required when campaign_id is not provided.",
"billing_type": "billing_type is required when campaign_id is not provided."
},
"status_code": 400
}
HTTP Status Codes
Success Codes
| Code | Used By | Description |
|---|---|---|
200 OK |
Retrieve session, cancel session, submit payment, verify mandate, validate IBAN, reconciliation endpoints | Successful operation. |
201 Created |
Create session | Session created successfully. |
Client Error Codes
| Code | Exception Class | Description |
|---|---|---|
400 Bad Request |
ValidationError |
Missing or invalid fields. Includes per-field details. |
401 Unauthorized |
AuthenticationFailed |
Invalid, revoked, or missing API key. |
404 Not Found |
— | Session token not found. |
409 Conflict |
ConflictError |
Resource state conflict (e.g., lead already has active session, session not in expected state). |
410 Gone |
SessionExpiredError |
Session has expired or is in a terminal state. |
429 Too Many Requests |
Throttled |
Rate limit exceeded. See Rate Limits. |
Server Error Codes
| Code | Description |
|---|---|
500 Internal Server Error |
Unexpected error during payment processing or mandate verification. |
Per-Endpoint Errors
POST /api/v1/payment-sessions/
| Code | Condition |
|---|---|
400 |
Missing required fields (direct mode without gateway_id, amount, currency, billing_type). Invalid campaign_id, offer, or gateway. |
401 |
Invalid or revoked API key. |
409 |
Lead already has an active payment session. |
429 |
Rate limit exceeded (50/second per API key). |
GET /api/v1/payment-sessions/{token}/
| Code | Condition |
|---|---|
404 |
Session token not found in any tenant. |
429 |
Rate limit exceeded (1/second per IP). |
POST /api/v1/payment-sessions/{token}/cancel/
| Code | Condition |
|---|---|
401 |
Invalid or revoked API key. |
404 |
Session token not found. |
409 |
Session is not in PENDING status. |
410 |
Session has expired or completed. |
429 |
Rate limit exceeded (50/second per API key). |
POST /api/v1/payment-sessions/{token}/pay/
| Code | Condition |
|---|---|
400 |
Missing iban or iban_holder_name. Invalid IBAN format. |
404 |
Session token not found. |
409 |
Session not in payable state (concurrent payment attempt). |
410 |
Session expired or completed. |
429 |
Rate limit exceeded (1/second per IP). |
500 |
Unexpected gateway error. |
POST /api/v1/payment-sessions/{token}/verify/
| Code | Condition |
|---|---|
404 |
Session token not found. |
409 |
Session not in AWAITING_VERIFICATION state. |
410 |
Session expired or completed. |
429 |
Rate limit exceeded (1/second per IP). |
500 |
Unexpected verification error. |
POST /api/v1/iban-validate/
| Code | Condition |
|---|---|
200 |
IBAN provided but invalid. Returns {"valid": false, "error": "..."}. |
400 |
Missing iban field. Returns {"valid": false, "error": "IBAN is required"}. Note: this response does not include the status_code field. |
401 |
Authentication required (no API key or session token). |
429 |
Rate limit exceeded (50/second per API key). |
GET /api/v1/reconciliation/transactions/
| Code | Condition |
|---|---|
400 |
Missing start_date. Invalid date format. Date range exceeds 90 days. |
401 |
Invalid or revoked API key. |
429 |
Rate limit exceeded (50/second per API key). |
GET /api/v1/reconciliation/chargebacks/
| Code | Condition |
|---|---|
400 |
Missing start_date. Invalid date format. Date range exceeds 90 days. |
401 |
Invalid or revoked API key. |
429 |
Rate limit exceeded (50/second per API key). |
GET /api/v1/reconciliation/subscriptions/
| Code | Condition |
|---|---|
400 |
Missing start_date. Invalid date format. Date range exceeds 90 days. |
401 |
Invalid or revoked API key. |
429 |
Rate limit exceeded (50/second per API key). |
Custom Exception Classes
| Class | Code | Default Detail |
|---|---|---|
ConflictError |
409 | Conflict |
SessionExpiredError |
410 | Session expired or completed |
Payment Decline Codes (error_details.code)
Distinct from HTTP-level errors: a gateway-declined payment returns 200 OK with status: "declined" (retryable) or status: "failed" (terminal), carrying a normalized error_details.code alongside the raw gateway values. Branch on the normalized code rather than string-matching error_details.gateway_message.
| Code | Meaning |
|---|---|
insufficient_funds |
Account has insufficient balance to cover the transaction. |
invalid_iban |
IBAN is malformed, unknown, or rejected at submission. |
mandate_rejected |
SEPA mandate missing, rejected, or unauthorized. |
account_closed |
Account is closed or blocked at the bank. |
do_not_honor |
Generic bank refusal without a specific reason code. |
gateway_unavailable |
Upstream gateway timeout or service outage. |
unknown |
No matching pattern — raw gateway message preserved in error_details.gateway_message. |
The taxonomy is stable. New codes will only be added, never renamed. See Submit Payment for sample payloads.
Authentication Error Details
| Scenario | Response |
|---|---|
No Authorization header |
{"detail": "Authentication credentials were not provided", "status_code": 401} |
| Invalid API key hash | {"detail": "Invalid or revoked API key", "status_code": 401} |
| Invalid session token | {"detail": "Invalid or expired session token", "status_code": 401} |