Skip to content
STONS

Error codes

This document lists every error.code that the public routes under /v1 and /checkout/v1 return, with the HTTP status the service uses and what the client should do. Read it when writing the error handling of an integration.

Error body

Every error response of the API has the same body:

{ "error": { "code": "route_not_found", "message": "Route not found" } }
Field Type Meaning
error.code string A machine-readable code from the code list. The service does not rename or remove codes; new codes are listed in the changelog
error.message string An explanation for humans, in English. The text may change: do not parse it in code
error.details array Present only when the request failed schema validation: one element per problem, with path — where the problem is (body.amount, params.id, or body for an unknown field) — and message. A validation_failed from a later check, for example of the decimals of an amount, has no details; its message starts with the field name

Every response, including errors, has the X-Request-Id header. Give its value to the operator when you report a problem: it finds the request in the logs. If the client sends its own X-Request-Id of 1–128 Latin letters, digits and the characters ., _, :, -, the service uses it; otherwise the service generates a UUID.

The OpenAPI description, GET /v1/openapi.json, declares one list of codes for all routes of the service, internal ones included. A public route returns only the codes from the list below.

Two kinds of responses do not have this body:

  • the HTTPS proxy answers a path outside /v1/ with 404 and an empty body;
  • while the API is shutting down, it answers 503 with a body in a different format.

Handle a response whose body is not in the format above by its HTTP status.

How to handle errors

  • 400, 413, 415 — the request is wrong. Fix it: the same request gets the same error.
  • 401 — check the key and the header name.
  • 403 — the merchant is disabled or not approved. Requests keep failing until the owner or the operator changes that.
  • 404 — check the path and the identifier.
  • 409 — the request conflicts with an existing object: an idempotency key (order_id, reference) is reused with different data, or a token choice on the checkout page is already made or no longer possible. Do not retry the same request.
  • 422 — the request is well-formed, but a business rule refuses it; the code names the rule. The same request gets the same error until the setting behind the rule changes.
  • 429 — retry after the number of seconds in the retry-after header.
  • 500, 503 — a problem of the service. Retry later with a growing delay and the same idempotency key: order_id for an invoice, reference for a payout. A retry does not create a second object.
  • A code that is not in the list — handle the response by its HTTP status class: 4xx is an error in the request, 5xx is a problem of the service.

Code list

HTTP error.code Routes When What the client does
400 validation_failed all The body, the path or the parameters fail validation: a field is missing, unknown, null or malformed. Or a later check of a field fails: the format or decimals of an amount, the precision of a price, external_user_id in per_user mode Fix the field named in details or at the start of message
400, 413, 415 bad_request routes with a request body The body is not valid JSON or is empty with Content-Type: application/json (400), is larger than 16 KiB (413), or has an unsupported Content-Type (415) Send a JSON object with Content-Type: application/json
401 unauthorized /v1 The X-Api-Key header is missing, or the key is malformed, unknown, revoked, or its secret does not match Check the key. The reason is deliberately not disclosed — see authentication
403 merchant_disabled /v1 The merchant is disabled — by the operator or by the owner in the cabinet (Dashboard) Turn the project on in the cabinet. If the operator turned it off, only the operator can turn it back on
403 merchant_not_approved POST /v1/invoices, POST /v1/addresses, POST /checkout/v1/invoices/{id}/select The merchant has not been approved after review yet, or was rejected Wait for the review. The owner sees the rejection reason in the cabinet and can fix the application and submit it again. A retry with the order_id of an existing invoice returns that invoice
404 route_not_found any path under /v1 or /checkout/v1 There is no route with this method and path Check the method and the path
404 invoice_not_found GET /v1/invoices/{id}, GET /checkout/v1/invoices/{id}, POST /checkout/v1/invoices/{id}/select The invoice does not exist or belongs to another merchant. On the /checkout/v1 routes also: the merchant is disabled Check the identifier
404 payout_not_found GET /v1/payouts/{id} The payout does not exist or belongs to another merchant Check the identifier
404 deposit_not_found GET /v1/deposits/{tx_hash} The transaction has no transfer to an address of the merchant: the service does not know it, it went only to other merchants, or its block has not been scanned yet Check the hash. For a transaction just sent, retry later — see verify a transfer
409 address_mode_mismatch POST /v1/addresses The merchant works in per_invoice mode Take the address from the invoice — see modes
409 order_id_conflict POST /v1/invoices An invoice with this order_id exists with a different asset, amount, currencies, external_user_id, return_url or cancel_url Check whether the order_id is reused for a different order — see idempotency
409 reference_conflict POST /v1/payouts A payout with this reference exists with a different asset, address or amount Check whether the reference is reused for a different payout
409 asset_already_selected POST /checkout/v1/invoices/{id}/select The invoice already has a different asset. The choice is final Show the customer the invoice with its token
409 invoice_expired POST /checkout/v1/invoices/{id}/select The deadline of the invoice has passed, or the invoice is no longer in the created status Show the customer the state of the invoice
422 address_mode_disabled POST /v1/addresses, POST /v1/invoices, POST /checkout/v1/invoices/{id}/select The platform has temporarily paused the address mode of the merchant Tell the customer that payment is not available now and retry later. A retry with the order_id of an existing invoice returns that invoice; transfers to addresses already issued are credited as usual — see a paused mode
422 mode_not_allowed POST /v1/addresses, POST /v1/invoices, POST /checkout/v1/invoices/{id}/select The address mode of this project is not allowed to it: an operator allows a mode per project (ADR-0066) Ask the operator. Transfers to addresses already issued are credited as usual
422 asset_not_supported POST /v1/invoices, POST /checkout/v1/invoices/{id}/select The asset is unknown or not accepted for payments Use an asset from the table of assets that pays invoices
422 asset_not_payable POST /v1/payouts The service makes no payouts in this asset: it is unknown, inactive, or for receiving only Use an asset from the table of assets that has payouts
422 asset_payments_disabled POST /v1/invoices, POST /checkout/v1/invoices/{id}/select The operator has temporarily disabled payments in the asset Offer another asset or retry later. A retry with the order_id of an existing invoice returns that invoice
422 asset_disabled_by_merchant POST /v1/invoices, POST /checkout/v1/invoices/{id}/select Payments in the asset are disabled in the project settings Offer another asset, or turn the token on in the "Tokens" section of the project in the cabinet. A retry with the order_id of an existing invoice returns that invoice
422 asset_payouts_disabled POST /v1/payouts The operator has temporarily disabled payouts in the asset Retry later with the same reference. Payouts created earlier are executed as usual
422 amount_below_minimum POST /v1/invoices, POST /checkout/v1/invoices/{id}/select The amount or the price is below the minimum deposit of the asset Increase the amount, or choose another asset
422 asset_required POST /v1/invoices There is no asset, and the installation has no checkout page where the customer could choose the token Pass asset
422 no_payable_assets POST /v1/invoices There is neither asset nor currencies, and the merchant currently accepts no asset that has an exchange rate Turn a token on in the project settings, or pass asset
422 asset_not_offered POST /v1/invoices, POST /checkout/v1/invoices/{id}/select The asset cannot be offered for a price in US dollars: the service has no exchange rate for it; on the choice route also: it is not in the invoice's currencies Choose another asset
422 redirect_url_not_allowed POST /v1/invoices return_url or cancel_url is not on a site of the merchant Use a URL on the project's site or on the site of the project's URL of the same kind — see return URLs
422 address_not_whitelisted POST /v1/payouts The address is not on the merchant's whitelist for this asset, or the delay before its first use has not passed. The message says which See where you can pay. The response does not depend on the amount
429 rate_limited /v1, /checkout/v1 A request limit is exceeded Retry after the number of seconds in the retry-after header — see rate limits
500 internal_error all An unhandled error of the service. The details go to the service log, not to the response Retry later with the same idempotency key. If it repeats, report the X-Request-Id
503 service_unavailable POST /v1/addresses, POST /v1/invoices, POST /checkout/v1/invoices/{id}/select The service cannot derive a new address in its current configuration; the message says why Retry later. If it persists, contact the operator
503 addresses_exhausted POST /v1/addresses, POST /v1/invoices, POST /checkout/v1/invoices/{id}/select The address derivation indexes have run out: all 2^31 are used. Practically unreachable Contact the operator
503 network_fee_quote_unavailable POST /v1/invoices The project still has the retired setting "the customer pays the network fee", so no new invoice is created. A retry with the order_id of an existing invoice returns that invoice The owner saves "Who pays the fees" in the project settings in the cabinet; then retry
503 rate_unavailable POST /v1/invoices, POST /checkout/v1/invoices/{id}/select There is no fresh exchange rate right now: when an invoice is created without asset, for none of the allowed tokens; at the choice, for the coin of the chosen asset. Nothing is created or recorded. A retry with the order_id of an existing invoice returns that invoice Retry later with a growing delay and the same order_id. On the checkout page, offer another token or ask the customer to try again later. If it persists, contact the operator
503 payouts_halted POST /v1/payouts Payouts are halted — in this asset or entirely; the message says which. The reason is not disclosed Retry later with the same reference. A retry of an existing reference returns that payout even during a halt

Examples

A path that does not exist:

curl -s https://api.stons.io/v1/unknown

404:

{"error":{"code":"route_not_found","message":"Route not found"}}

A request to POST /v1/invoices with "expires_in": 60 fails schema validation, and details lists every problem field:

{
  "error": {
    "code": "validation_failed",
    "message": "Request validation failed",
    "details": [{ "path": "body.expires_in", "message": "Too small: expected number to be >=300" }]
  }
}

A request with "asset": "USDT_TRON" and "amount": "49.9000001" passes schema validation and fails the check of decimals, so there is no details:

{
  "error": {
    "code": "validation_failed",
    "message": "amount: amount has more than 6 digits after the decimal point (USDT_TRON has 6 decimals)"
  }
}