Errors & reliability
The SDK is built for a payments flow, so it handles transient failures safely and gives you precise errors to act on.
Retries
Transient failures are retried automatically with exponential backoff and jitter:
- Network errors and timeouts
429Too Many Requests (honors theRetry-Afterheader)5xxserver errors
Only safe requests retry: reads (GET), and mutations that carry an
idempotency key. Default is 2 retries; configure with maxRetries.
new ShoppableCheckout({ token: "pk_live_…", maxRetries: 3, timeout: 15000 });Idempotency
Every mutating call (prepareCheckout, createIntent, updateCheckout,
completeCheckout) sends an auto-generated Idempotency-Key, so an automatic
retry can never create a duplicate order or charge. Pass your own key to make a
retry idempotent across separate invocations (e.g. a user-triggered retry):
await checkout.prepareCheckout(cart, { idempotencyKey: myStableKey });Timeouts and cancellation
Each request times out after timeout ms (default 30000) and accepts an
AbortSignal:
const controller = new AbortController();const promise = checkout.completeCheckout(id, { signal: controller.signal });controller.abort(); // rejects with an AbortError, never retriedError types
All API failures throw a subclass of ShoppableApiError. Catch the base to handle
everything, or a subclass for finer control. Every error carries status, the
encrypted support ref (when present), the server requestId, and the raw body.
| Error | When |
|---|---|
ShoppableAuthError | 401 token missing/invalid/expired |
ShoppableValidationError | 400 / 422 invalid request |
ShoppableNotFoundError | 404 |
ShoppableRateLimitError | 429 (has retryAfter in ms) |
ShoppableServerError | 5xx |
ShoppableNetworkError | no response (offline, DNS, TLS) |
ShoppableTimeoutError | exceeded the timeout |
ShoppableConfigError | SDK misconfiguration (not an API error) |
import { ShoppableApiError, ShoppableRateLimitError } from "@shoppable/checkout";
try { await checkout.prepareCheckout(cart);} catch (err) { if (err instanceof ShoppableRateLimitError) { // back off and retry later } else if (err instanceof ShoppableApiError) { showError(err.message, err.ref); // quote err.ref / err.requestId to support }}