DocsErrors

Errors

The Skiro API returns conventional HTTP status codes and a JSON body that explains what went wrong.

Error format

Every error response has the same shape:

{
  "error": "invalid_amount",
  "message": "amount must be greater than 0",
  "status_code": 400
}
  • error: a stable, machine-readable code. You can switch on this in your code.
  • message: a human-readable description. The wording may change; don't parse it.
  • status_code: matches the HTTP status, included for convenience.

HTTP status codes

CodeMeaning
200Success.
201Resource created.
204Success with no body (typically a delete).
400Bad request. Your input is malformed or invalid.
401Unauthorized. Missing or invalid API key.
403Forbidden. Your key is valid but lacks permission.
404Not found. The resource doesn't exist or isn't visible to your account.
409Conflict. The resource already exists in a conflicting state.
429Too many requests. You're rate limited. Back off.
500Internal server error. Something on our end. Retry with backoff.
503Temporarily unavailable. We're overloaded or in maintenance. Retry.

Error codes

Validation

  • missing_field: a required parameter is missing
  • invalid_amount: amount must be a positive number
  • invalid_currency: currency code is not supported
  • invalid_payout_currency: payout currency is not supported
  • invalid_email: email format is malformed
  • invalid_url: URL must be HTTPS in live mode

Authentication

  • missing_api_key: no Authorization header provided
  • invalid_api_key: key not found or has been revoked
  • wrong_environment: using a test key in live mode (or vice versa)

Resource

  • session_not_found: checkout session ID doesn't exist
  • session_expired: session passed its 1-hour window
  • session_already_completed: can't expire a session that's already paid
  • transaction_not_found: transaction ID doesn't exist

Limits and rates

  • rate_limited: too many requests too fast
  • daily_limit_exceeded: you've hit your account's daily volume cap
  • monthly_limit_exceeded: you've hit your monthly cap

Account state

  • account_suspended: your account is currently suspended
  • kyc_required: identity verification needed before processing
  • wallet_not_configured: set a payout wallet before creating sessions

Handling errors

Every non-2xx response includes a JSON body with an error code and message:

{
  "error": "invalid_amount",
  "message": "amount must be greater than 0"
}

Match on error in your code, not on message (the message text may change):

const res = await fetch('https://api.skiro.io/v1/checkout', { /* ... */ })
const json = await res.json()

if (!res.ok) {
  switch (json.error) {
    case 'invalid_amount':
      return showError('Amount must be greater than 0.')
    case 'rate_limited':
      return retryAfter(res.headers.get('retry-after'))
    default:
      return showError('Something went wrong.')
  }
}
Retry strategy
For 5xx errors and 429 rate limits, retry with exponential backoff. Start at 1 second and double up to 60 seconds. Give up after 5 attempts and surface the error to the user.
Don't retry on 4xx errors
4xx errors mean your request was malformed. Retrying without changes will fail again. Fix the request first.
Last updated: May 30, 2026