Errors

Every error the Railbed API returns, with its HTTP status, what caused it and what to do next.

On this page

The error object

Every error has an HTTP status of 400 or above and the same JSON body:

An error
{
  "error": {
    "code": "no_payout_wallet",
    "message": "Add a payout wallet in the dashboard before taking live payments."
  }
}
409 Conflict
FieldTypeDescription
codestringStable and machine-readable. Branch on this
messagestringA sentence for people. It can be reworded at any time, so show it or log it, but never parse it
fieldstringThe request field at fault, when there is one: amount, metadata, order.items[0].amount, starting_after

Handling errors

Decide by status first, then by code where it matters:

StatusMeaningWhat your code should do
400The request was invalidFix the request. Retrying it unchanged fails the same way
401The key or agent access token is missing, malformed, expired or revokedFor a secret key, check the key and its mode, and don't retry. An agent gets a new access token on invalid_token, and asks its person to connect it again on agent_disconnected
403Not allowed for this account, key or agentDon't retry. On insufficient_scope, use a key or agent with Full access
404Not found in this account and modeCheck the id and whether the key's mode matches the object's
409The object's state doesn't allow thisRead the object and act on its current status
410The session can't be paid any moreCreate a new session
413, 415The body is too large or isn't JSONFix the request
429Rate limitedWait for Retry-After seconds, then retry
500, 502, 503Something failed on our side or upstreamRetry with backoff. Creates are safe to retry with the same Idempotency-Key
A small error handler
async function railbed(path, init = {}) {
  const res = await fetch(`https://pay.railbed.io/v1${path}`, {
    ...init,
    headers: {
      Authorization: `Bearer ${process.env.RAILBED_SECRET_KEY}`,
      'Content-Type': 'application/json',
      ...init.headers,
    },
  });
  const body = await res.json();
  if (res.ok) return body;
  const err = Object.assign(new Error(body.error.message), {
    status: res.status,
    code: body.error.code,
    field: body.error.field,
  });
  err.retryable = res.status === 429 || res.status >= 500;
  err.retryAfter = Number(res.headers.get('Retry-After')) || null;
  throw err;
}

Every code

400 Bad Request

CodeEndpointCause
invalid_jsonAny with a bodyThe body isn't a JSON object
invalid_amountCreateamount isn't a string, or isn't from "1.00" to "100000.00" with at most two decimal places
invalid_currencyCreatecurrency isn't USD, EUR, GBP, CAD or AUD
missing_descriptionCreatedescription is missing, blank or not a string
too_longCreatedescription or reference is over 120 characters. See field
invalid_referenceCreatereference isn't a string
invalid_emailCreate, Startcustomer_email or customer.email isn't a valid address, or Start needs one and none is on the session
invalid_urlCreatesuccess_url or cancel_url isn't a full http(s):// address, has a username or password, is over 1,000 characters, or isn't https:// in Live mode
invalid_metadataCreateOver 20 keys, a blank key, a key over 40 characters or starting with __, or a value that isn't a string of at most 500 characters
invalid_idempotency_keyCreateThe Idempotency-Key header is blank, over 120 characters or has characters other than printable ASCII
invalid_customerCreatecustomer isn't an object, or customer.email and customer_email are different addresses
invalid_orderCreateA field in order has the wrong type, is too long or is out of range, such as a quantity that isn't a whole number from 1 to 100,000 or an item without name or amount. See field
invalid_order_totalsCreateThe items less discount plus shipping and tax don't equal amount to the cent, or without items, shipping and tax less discount come to more than amount
invalid_countryStartcountry isn't a two-letter code such as US
invalid_limitAny listlimit isn't a whole number from 1 to 100
invalid_cursorAny liststarting_after isn't one of your payments, orders or customers (whichever you're listing) in this mode
invalid_outcomeSimulateoutcome isn't paid, underpaid, declined or failed
invalid_fulfillmentUpdate an orderfulfillment is missing or isn't none, unfulfilled or fulfilled

401 Unauthorized

Every 401 carries a WWW-Authenticate header that points to the API's protected resource metadata, where an agent can find out how to get access.

CodeCause
invalid_api_keyNo Authorization: Bearer header, a malformed key, or a revoked one. Create a key in Developers
invalid_tokenAn agent's access token expired, is malformed, or wasn't issued for Railbed. Get a new one from the token endpoint described in auth.md
agent_disconnectedThe agent was disconnected in the dashboard, or the person who connected it no longer manages keys for the business. Ask an owner or developer to connect it again

403 Forbidden

CodeCause
suspendedThe account is suspended. Email support@railbed.com
insufficient_scopeThe key's or agent's access level doesn't include the scope this endpoint needs, such as a Read only key creating a checkout session. The message and the WWW-Authenticate header (error="insufficient_scope", scope="…") name the scope. Use a key or agent with Full access
live_paymentSimulate was called on a Live payment. Only Test payments can be simulated

404 Not Found

CodeCause
not_foundNo such session, payment, order or customer in your account in this key's mode, or no such endpoint. A Test key can't see Live objects and the other way round

409 Conflict

CodeEndpointCause
idempotency_conflictCreateThe key was used with a different body. Rarely, the first request with the key hadn't finished saving: retry in a moment
no_payout_walletCreateLive mode needs a payout wallet. Add one in Settings
order_paidCreateThe store order in order.id is already paid, or held with money you could accept as paid or that part-paid it. Nothing was created. Check the order before asking the buyer to pay again. See Store orders and retries
unavailableStartThe account can't take payments now
no_providersStartNo card provider can take this amount and currency right now. Nothing was started; try later or a different amount
already_paidStartThe payment is complete
heldStartThe money arrived and the payment is held for review
failedStartThe payment was declined. Create a new session
not_startedSimulateStart the session before simulating

410 Gone

CodeCause
expiredThe session passed its expires_at without being paid
canceledThe payment link was canceled in the dashboard

Size, type and rate

StatusCodeCause
413request_too_largeThe body is over 64 KiB
415unsupported_media_typeThe body isn't sent as Content-Type: application/json
429rate_limitedOver about 120 requests a minute (each connected agent has its own 120, and a business's agents share 600), 12 starts a minute, or about 60 requests a minute from one address with a missing or invalid key. Wait for Retry-After

5xx

StatusCodeCause
500internalSomething failed on our side. Retry with backoff; email support@railbed.com if it continues
502network_unavailableThe card network didn't answer while starting. Retry in a minute
503misconfiguredCard payments are briefly unavailable. Retry later

Updated · This page as Markdown