Errors & limits
Every failed request returns the same JSON shape with a stable integer errorCode. Drive your control flow with the code, not the message.
Error shape
Every failed request returns the same JSON: errorCode — a stable integer for machine handling (it never changes across locales or message rewording; drive your control flow with it), message — a human-readable description for logs, and optional args — the values interpolated into the message template, in placeholder order.
{
"errorCode": 1042,
"message": "amount must be greater than 0",
"args": ["amount"]
}const res = await fetch(url, { method: "POST", headers, body });
if (!res.ok) {
const err = await res.json();
// branch on err.errorCode — never on err.message
if (res.status === 409) return existingPaymentFor(externalId);
if (res.status === 429) return retryWithBackoff();
throw new Error(`suward ${res.status} ${err.errorCode}: ${err.message}`);
}HTTP status codes
| Code | Meaning | What to do |
|---|---|---|
400 | Invalid request | Fix the payload. Retrying unchanged will fail again. |
401 | Missing or invalid API key | Check the X-Api-Key header and that the key isn't revoked. |
404 | Not found | Wrong id, or a resource in another project. |
409 | Conflict — e.g. an externalId you've already used | Fetch the existing resource instead of creating another. |
429 | Rate limited | Back off and retry. |
5xx | Server-side failure | Retry with backoff. Create calls carrying externalId are safe to retry. |
Limits
- Rate limits are enforced per API key; on
429, back off and retry. Higher throughput is available — contact payments@suward.com. - A single payment is capped at the equivalent of $60,000. Split larger amounts across payments, or talk to us about your flow.
- Pagination:
limitmax 100 per page. - Metadata: up to 4 KiB per resource.
- There is no
Idempotency-Keyheader — idempotency of create calls rides on yourexternalId(a duplicate returns409instead of creating a second resource).
Retrying safely
| Situation | Safe to retry? | Why |
|---|---|---|
| Create call timed out | Yes — with the same externalId | A duplicate never creates a second resource. |
429 on any call | Yes, with backoff | Rate limits are per key and transient. |
5xx on a create call | Yes — with the same externalId | Same idempotency guarantee. |
400 | No | The payload is wrong; retrying repeats the mistake. |
409 on create | No — read instead | The resource already exists. Fetch it by your externalId. |
Errors your handler will meet in production
A screening-rejected payment is not an API error — it is a payment in failed / complianceRejected. Same for expiry and cancellation. Don't route lifecycle outcomes through
your error path. → Payment lifecycle & statuses
Next: conventions shared by all endpoints → API reference.
Something unclear? payments@suward.com — we treat every integration question as a documentation defect.