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.

error.json
{
	"errorCode": 1042,
	"message": "amount must be greater than 0",
	"args": ["amount"]
}
handle.ts
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

CodeMeaningWhat to do
400Invalid requestFix the payload. Retrying unchanged will fail again.
401Missing or invalid API keyCheck the X-Api-Key header and that the key isn't revoked.
404Not foundWrong id, or a resource in another project.
409Conflict — e.g. an externalId you've already usedFetch the existing resource instead of creating another.
429Rate limitedBack off and retry.
5xxServer-side failureRetry with backoff. Create calls carrying externalId are safe to retry.
[PLACEHOLDER: the full errorCode registry — rendered from the product error registry at build time; a hand-written list is forbidden because it will go stale]

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: limit max 100 per page.
  • Metadata: up to 4 KiB per resource.
  • There is no Idempotency-Key header — idempotency of create calls rides on your externalId (a duplicate returns 409 instead of creating a second resource).

Retrying safely

SituationSafe to retry?Why
Create call timed outYes — with the same externalIdA duplicate never creates a second resource.
429 on any callYes, with backoffRate limits are per key and transient.
5xx on a create callYes — with the same externalIdSame idempotency guarantee.
400NoThe payload is wrong; retrying repeats the mistake.
409 on createNo — read insteadThe 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.