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
| Code | Meaning |
|---|---|
200 | Success. |
201 | Resource created. |
204 | Success with no body (typically a delete). |
400 | Bad request. Your input is malformed or invalid. |
401 | Unauthorized. Missing or invalid API key. |
403 | Forbidden. Your key is valid but lacks permission. |
404 | Not found. The resource doesn't exist or isn't visible to your account. |
409 | Conflict. The resource already exists in a conflicting state. |
429 | Too many requests. You're rate limited. Back off. |
500 | Internal server error. Something on our end. Retry with backoff. |
503 | Temporarily unavailable. We're overloaded or in maintenance. Retry. |
Error codes
Validation
missing_field: a required parameter is missinginvalid_amount: amount must be a positive numberinvalid_currency: currency code is not supportedinvalid_payout_currency: payout currency is not supportedinvalid_email: email format is malformedinvalid_url: URL must be HTTPS in live mode
Authentication
missing_api_key: no Authorization header providedinvalid_api_key: key not found or has been revokedwrong_environment: using a test key in live mode (or vice versa)
Resource
session_not_found: checkout session ID doesn't existsession_expired: session passed its 1-hour windowsession_already_completed: can't expire a session that's already paidtransaction_not_found: transaction ID doesn't exist
Limits and rates
rate_limited: too many requests too fastdaily_limit_exceeded: you've hit your account's daily volume capmonthly_limit_exceeded: you've hit your monthly cap
Account state
account_suspended: your account is currently suspendedkyc_required: identity verification needed before processingwallet_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.