Error Reference
All API errors return a JSON body with an errors array. Each element carries
a detail string and, on some endpoints, a machine-readable code. Validation
failures list one element per field.
The normative definition is §10 of the protocol specification.
The error that arrives as a success
Routing, limits and anti-fraud do not refuse a payment with an HTTP error. They create the
transaction and mark it rejected, so the response is 201 Created with a perfectly ordinary
body:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "rejected",
"error_description": "terminal not found"
}
Code that checks only the status code books unpaid orders as paid. Read status on every
response. rejected and failed mean no money moved; charged is the only settled outcome.
Error response format
{
"errors": [
{ "code": "flow_not_continuable", "detail": "status_poll" }
]
}
A few endpoints predating this format answer with {"error": "…", "message": "…"} instead. Accept both shapes.
HTTP status codes
| Code | Meaning | Common cause |
|---|---|---|
400 | Bad Request | Malformed JSON, missing required fields — and, on some endpoints, a missing x-organization-id header |
401 | Unauthorized | Missing or invalid credentials |
Handle 400 and 401 the same way when they carry no field pointers: both mean the request was
not authenticated, and which one you get depends on the endpoint.
| 403 | Forbidden | Valid credentials but no permission for this resource |
| 404 | Not Found | Transaction or resource with given ID does not exist |
| 409 | Conflict | The transaction cannot move as requested from its current state |
| 422 | Unprocessable Entity | Validation failed — one errors element per field |
| 429 | Too Many Requests | Rate limit exceeded |
| 500 | Internal Server Error | Platform error — contact support with transaction ID |
Transaction error codes
These appear in error_description within webhook payloads and transaction status responses:
| Code | Meaning | Recommended action |
|---|---|---|
insufficient_funds | Card declined: insufficient funds | Ask payer to use another card |
card_declined | Card declined by issuer | Ask payer to contact their bank |
expired_card | Card expiry date has passed | Ask payer to update card details |
invalid_card_number | Card number is invalid | Check card number format |
invalid_cvv | CVV is incorrect | Ask payer to re-enter CVV |
do_not_honour | Generic bank decline | Ask payer to contact their bank |
lost_card | Card reported lost | Do not retry |
stolen_card | Card reported stolen | Do not retry |
restricted_card | Card cannot be used for this transaction | Ask payer to use another card |
transaction_limit_exceeded | Amount exceeds card/account limits | Ask payer to use another payment method |
3ds_failed | 3D Secure authentication failed | Ask payer to retry or use another card |
3ds_timeout | 3D Secure timed out | Retry the transaction |
provider_unavailable | Payment provider is temporarily unavailable | Retry after a delay |
routing_failed | No provider found for this transaction | Contact your platform operator |
duplicate_txid | txid already exists for this terminal | Use a unique txid per transaction |
currency_not_supported | Currency not supported by this terminal | Check terminal configuration |
amount_too_low | Amount below minimum for this provider | Increase transaction amount |
amount_too_high | Amount above maximum for this provider | Split into smaller transactions |
antifraud_reject | Blocked by anti-fraud rules | Contact your platform operator |
Validation errors (422)
Validation errors list one errors element per problem, each with a detail
string naming the field:
{
"errors": [
{ "detail": "amount: can't be blank" },
{ "detail": "currency: is invalid" }
]
}
Handling errors in your code
Retry strategy
| Error type | Retry? | Notes |
|---|---|---|
provider_unavailable | ✅ Yes | Wait 30–60 seconds, max 3 retries |
3ds_timeout | ✅ Yes | Retry once immediately |
insufficient_funds | ❌ No | Show alternative payment method |
card_declined | ❌ No | Show alternative payment method |
lost_card, stolen_card | ❌ Never | Do not retry or re-present |
duplicate_txid | ❌ No | Generate a new unique txid |
routing_failed | ❌ No | Contact your platform operator |
Example error handling (Python)
import requests
response = requests.post(
'https://4pay.online/api/v1/transactions',
headers={
'x-api-key': 'YOUR_API_KEY',
'x-organization-id': '00000000-1111-2222-3333-444444444444',
'Content-Type': 'application/json',
},
json={'params': {'type': 'payment', 'amount': '1000', 'currency': 'USD',
'txid': 'order-1', 'env': 'prod'}}
)
if response.status_code == 422:
for err in response.json().get('errors', []):
print(err.get('detail'))
elif response.status_code == 409:
print("The transaction cannot move as requested from its current state")
elif response.status_code >= 500:
print("Platform error — retry later or contact support")
else:
tx = response.json()
print(f"Transaction created: {tx['id']}")
Contact support
For persistent errors or unexpected 500 responses, contact:
- Email: support@4pay.online
- Include the transaction
id(UUID) for faster diagnosis