Skip to main content
Every failure from @agentaos/pay, a bad request, an auth problem, a network blip, throws a subclass of AgentaOSError. Retries for transient failures (5xx, network errors, some 429s) happen automatically inside the SDK before anything is thrown, so if you see an error, it’s already survived the retry budget.

Hierarchy

Every SDK-thrown error extends AgentaOSError, so err instanceof AgentaOSError catches all of them, err.status, err.code, and err.requestId (from the response’s x-request-id header, when present) are always there for logging.

Catching errors

Catch specific subclasses before the generic AgentaOSError, they all extend it, so a broad catch first would shadow the specific ones:

Retry behavior

Retries are controlled by the maxRetries client option (default 2), and only apply to checkouts, paymentLinks, transactions, invoices, subscriptions, and customers, webhooks.verify() never touches the network.
1

5xx responses

Retried with exponential backoff: min(1000 × 2^attempt, 10000) ms, so 1s, 2s, 4s, 8s, capped at 10s. After maxRetries is exhausted, throws ApiError.
2

429 (rate limited)

Retried once inline, and only if the response’s Retry-After header is 60 seconds or less and a retry attempt remains. If Retry-After exceeds 60s, or retries are exhausted, it throws RateLimitError immediately with retryAfter set from the header (in ms, defaulting to 60000 if the header is missing).
3

Network errors

Same exponential backoff as 5xx. After maxRetries, throws a generic AgentaOSError (code: 'network_error', status: 0).
4

Timeouts

Never retried. Each attempt (including retries of other error types) gets its own timeout-ms AbortController; if it fires, TimeoutError is thrown immediately, no backoff, no further attempts.
Because every attempt gets a fresh timeout window, worst-case latency for a single call is roughly (maxRetries + 1) × timeout, plus backoff delay between attempts. With the defaults (timeout: 30000, maxRetries: 2), a call that keeps hitting 5xx can take up to roughly 90 seconds plus a few seconds of backoff before it finally throws.

Idempotency

Every POST the SDK sends (checkouts.create(), paymentLinks.create(), subscriptions.cancel(), and the rest) automatically carries an idempotency-key header, a random UUID generated fresh per call. Retry the same call with the same key and the server does not reject it: it replays the original success and hands back the same resource, including its already-issued invoice, if any, instead of creating a second one. There is no global 409 on a duplicate key. subscriptions.cancel() is a POST, so it carries an auto-generated key like any other. You rarely need to think about it: the call is safe to retry because cancelling an already-cancelled subscription is a no-op on the server, not an error. The key is sent, but the operation is naturally idempotent either way. GET reads carry no key, they’re naturally idempotent.

Debugging

Set debug: true on the client to log every request and retry to stderr (or your own logger):
Debug logs are sanitized: they never include your API key or request/response bodies, only method, path, status, timing, and retry/backoff notices.

Next steps

Overview

Client options, auth, and the resource map.

Webhooks

WebhookVerificationError and signature verification in detail.