Error Codes · Geat.io API

Error codes

Every error the v1 surface emits uses the envelope from README.md:

{ "success": false, "error": "Human-readable message.", "code": "MACHINE_CODE" }

Branch on code. The error prose may be reworded at any time.

The one exception is 429: no custom handler is registered for rate limiting, so a throttled request answers the limiter's default response rather than this envelope. Treat the status code as the contract there.

The table below lists every code emitted from v2/backend/app/api/v1/. "Surface" says where it can appear: auth = the API-key decorator (so any merchant endpoint), invoices = /v1/invoices, addresses = /v1/addresses, pay = /v1/pay.

CodeStatusSurfaceMeaning
ACCOUNT_INACTIVE403authThe API key's owning account is not active.
ACCOUNT_SUSPENDED403authThe API key's owning account is suspended.
ALREADY_REPORTED409payThis invoice has already been reported.
ALREADY_REVIEWED409payA review already exists for this invoice.
AMBIGUOUS_COIN400addresses, payThe coin symbol exists on several networks here; send network. The message lists them.
COIN_LOCKED409payA different coin is already locked for this invoice and cannot be changed.
COIN_NOT_ACCEPTED400addresses, payThe coin is not enabled on the profile, or is not active.
COIN_NOT_PAYABLE400addresses, payThe coin has no watch-service mapping or confirmation threshold, or the merchant has no active xpub for its chain.
CONFLICT409addresses, payA concurrency conflict with no readable winner. Nothing was charged - retry.
EMAIL_EXISTS409payAn email was already submitted for this invoice.
EMAIL_NOT_VERIFIED403authThe API key's owning account has not verified its email address.
EXPIRED400payThe invoice payment window has passed.
GAS_TOO_HIGH400payCurrent L1 Ethereum gas makes this invoice amount uneconomic.
IDEMPOTENCY_KEY_REUSED409addressesThe Idempotency-Key was already used for a different profile/coin combination.
INSUFFICIENT_CREDITS402addressesThe merchant has no credits left. Top up to create more addresses.
INVALID_CURRENCY400invoicesThe requested fiat currency is not supported/active.
INVALID_EMAIL400invoices, payNot a valid email address.
INVALID_FIELD400payA note or comment longer than 2000 characters.
INVALID_IDEMPOTENCY_KEY400addressesThe Idempotency-Key header exceeds 255 characters.
INVALID_INVOICE_ID400invoicesinvoice_id is over 255 characters or uses characters outside A-Za-z0-9._-.
INVALID_PRICE400invoicesprice is not a positive finite number up to 1,000,000.
INVALID_PROFILE_TYPE400invoices, addressesThe endpoint is only available for payment profiles.
INVALID_RATING400payRating is not an integer between 1 and 5.
INVALID_REASON400payNo report reason exists with that reason_id.
INVALID_STATUS400payThe payment session is completed, cancelled, failed or expired.
INVALID_URL400invoices, addressesA URL field is malformed, or a callback_url is not a valid public URL.
MERCHANT_UNAVAILABLE403payThe merchant cannot accept this payment. Deliberately does not say why: it covers suspended, inactive, deleted and out-of-credits.
MISSING_FIELDS400invoices, addresses, payA required field is absent or blank.
NO_COINS400invoicesThe profile has no accepted coins configured.
NO_RATE400payNo usable (non-stale) exchange rate for the selected coin.
NOT_COMPLETED400payReviews can only be submitted for completed invoices.
NOT_FOUND404invoices, addresses, payUnknown or malformed UUID, soft-deleted record, or a record owned by another account. Ownership misses are deliberately indistinguishable from "does not exist".
NOT_INITIALISED400payThe invoice payment session has not been created yet.
NOT_RECONCILABLE409invoicesNo selected coin, no payable watched address, or the invoice was cancelled.
PROFILE_INACTIVE403invoices, addressesThe target profile is not active.
PROFILE_SUSPENDED403invoices, addressesThe target profile is suspended.
RATE_ERROR500payThe crypto amount could not be calculated from the rate.
RATE_UNAVAILABLE503invoicesThe price could not be converted to USD - exchange rates are temporarily unavailable.
TURNSTILE_FAILED400payCloudflare Turnstile human verification failed.
UNAUTHORIZED401authMissing, unknown or revoked API key.
WATCH_REGISTRATION_FAILED502addresses, payPayment monitoring could not be registered. Everything is rolled back, including the claimed credit.
WATCH_UNAVAILABLE502invoicesThe watch service could not be reached, or the invoice has no active watch.

Notes on specific behaviours

  • ·404 over 403 for ownership. Reading another merchant's invoice or address request returns NOT_FOUND, not a permission error. A malformed UUID returns the same thing, so probing tells an attacker nothing.
  • ·Payer endpoints never leak merchant state. MERCHANT_UNAVAILABLE is the single public answer for suspended, inactive, deleted and out-of-credits merchants; the real reason is logged server-side and sent to the merchant's own notification channels.
  • ·Rollback on failure is complete. When address or coin selection fails after a credit was claimed (WATCH_REGISTRATION_FAILED, a lost idempotency race), the transaction is rolled back: the credit is refunded and no watch is left pointing at a row that does not exist.