Sandbox. Test keys, Arc testnet and test USDC. No real money moves here.

API reference

Errors

Errors use conventional HTTP status codes and always return the same JSON shape, so you can handle them in one place.

HTTP/1.1 400 Bad Request
{
  "error": {
    "type": "invalid_request_error",
    "code": "parameter_invalid",
    "message": "line_items must add up to amount.",
    "param": "line_items"
  }
}

The error object

typestring
The category: decides how your code should react.
codestring
A stable, machine-readable code. Branch on this.
messagestring
A human-readable explanation. It may change; don't parse it.
paramstring, optional
The parameter at fault, for parameter_invalid. Nested fields look like line_items[0].unit_amount.

Error types

typeRetry?Meaning
invalid_request_errorNo — fix the requestBad input, or not allowed in the session's state.
authentication_errorNo — fix the keyThe key is missing, invalid or for the other environment.
permission_errorNoThe account can't use the API right now.
not_found_errorNoThe object doesn't exist for this business.
rate_limit_errorYes, with backoffToo many requests.
api_errorYes, with backoffA problem on SaphraONE's side.

Error codes

HTTPtypecodeMeaning
400invalid_request_errorparameter_invalidA parameter is missing or invalid. param names it.
400invalid_request_errorbody_invalidThe body isn't a JSON object.
401authentication_errorapi_key_missingNo secret key was sent.
401authentication_errorapi_key_invalidThe key is unknown or revoked.
401authentication_errorapi_key_wrong_modeThe key belongs to the other environment.
403permission_erroraccount_not_businessThe account isn't a Business account.
403permission_errorbusiness_not_verifiedLive payments need a verified business.
404not_found_errorresource_missingNo such session (or it belongs to another business).
409invalid_request_errorsession_state_invalidThe session can't do that in its current state.
429rate_limit_errorrate_limitedToo many requests. Back off and retry.
500api_errorinternal_errorSomething failed on our side. Retry with the same Idempotency-Key.

Amount limits from the payment engine (for example below 0.50) come back as parameter_invalid with param: "amount".

Handling errors

async function withRetry(call, attempts = 4) {
  for (let attempt = 0; ; attempt += 1) {
    try {
      return await call();
    } catch (error) {
      const retryable = error.status === 429 || error.status >= 500 || error.status === undefined;
      if (!retryable || attempt + 1 >= attempts) throw error;
      const delay = 2 ** attempt * 1000 + Math.random() * 250;
      await new Promise((resolve) => setTimeout(resolve, delay));
    }
  }
}

// Safe: the Idempotency-Key makes the retried create return the same session.
const session = await withRetry(() =>
  saphra("/checkout/sessions", { method: "POST", headers: { "Idempotency-Key": "order-1042" }, body }),
);