Get started
Errors
Every failure returns the same flat shape, so you can handle them in one place.
The error shape
curl -X POST https://sandbox-api.baxpay.com/v1/payment-links \
-H "Authorization: Bearer $BAXPAY_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{ "description": "Refund", "amount": -5, "businessWide": true }'{
"statusCode": 400,
"message": ["amount must not be less than 0.01"],
"error": "Bad Request",
"timestamp": "2026-07-16T14:02:10.000Z",
"path": "/v1/payment-links"
}| Field | Always there | Meaning |
|---|---|---|
statusCode | yes | The HTTP status, repeated in the body |
message | yes | A string, or an array of strings when several fields failed validation. Written for a human reading a log: show it, don't parse it |
error | yes | The status name (Bad Request, Not Found, …) |
timestamp, path | yes | When and where it failed |
code | sometimes | A stable, machine-readable reason (e.g. EREPORTING_PERIOD_INVALID). Branch on it when present |
params, violations, reason, retryAfterSeconds | sometimes | Details for that code, such as each failed EN 16931 rule in violations |
requestId | with an API key, always | The request id, also in the X-Request-Id header; yours if you sent X-Request-ID. Find the call in the request log |
Handle message as either type:
const reason = Array.isArray(err.message) ? err.message.join('; ') : err.message;Status codes
| Status | Meaning | What to do |
|---|---|---|
200 201 204 | Success | Nothing |
400 | Invalid request: validation, a business rule, or a missing/reused Idempotency-Key | Fix the request. Retrying won't help |
401 | Missing, unknown or revoked credential | Check the key. API keys don't expire; a revoked one must be replaced |
403 | Authenticated but not allowed, e.g. no BaxPay plan chosen yet, or a feature your plan doesn't include | Read message; don't retry |
404 | No such resource in this key's mode | Check the id, and that a test key isn't looking for a live record |
409 | The same Idempotency-Key is still being processed | Wait briefly and retry with the same key |
429 | Rate limited (see limits) | Wait for X-RateLimit-Reset, then retry |
500 502 503 | Our side, or a payment partner | Retry with backoff and the same idempotency key |
A record from the other mode answers exactly like one that doesn't exist:
curl https://sandbox-api.baxpay.com/v1/payment-links/00000000-0000-4000-8000-000000000000 \
-H "Authorization: Bearer $BAXPAY_API_KEY"{ "statusCode": 404, "message": "Payment link not found", "error": "Not Found" }curl https://sandbox-api.baxpay.com/v1/payment-links \
-H "Authorization: Bearer bx_test_0000"{ "statusCode": 401, "message": "Invalid or revoked API key", "error": "Unauthorized" }Retrying safely
A network timeout tells you nothing about whether the call went through. That's what idempotency keys are for.