Skip to content

Errors

Every failure is JSON with the same shape. There is no other error format.

{
"error": {
"type": "invalid_request_error",
"code": "mint_not_allowed",
"message": "That token is not accepted on this platform.",
"param": "mint"
}
}

Branch on code, never on message. Codes are contract; messages are written for humans and will be reworded. param names the offending field when one field is at fault.

type is a coarse grouping and does not map cleanly onto the status: session_required is a 403 with type invalid_request_error, not authentication_error. Branch on code and read type as a hint only.

The single most useful distinction, because getting it wrong either loses money or duplicates it.

Status Retry?
400, 403, 404, 409 No. The request is wrong or the state has moved on. Retrying sends the identical request and gets the identical answer.
401 Only after fixing credentials. A retry with the same key fails identically.
429 Yes, after retry-after seconds. The response carries retry-after and ratelimit-* headers.
500 Yes, with backoff, and with an Idempotency-Key on anything that creates.
413 No. Send less.
Code Status What it means
unauthenticated 401 No API key and no session. Send authorization: Bearer sk_... or sign in.
invalid_credentials 401 The email or password is wrong. Deliberately does not say which.
insufficient_scope 403 A read-only key attempted a write. Scope is enforced by HTTP method: read keys may only GET.
session_required 403 This route refuses API keys and needs a signed-in human. It guards /v1/payout_wallet, so a stolen key cannot redirect where your money lands.
owner_required 403 A platform-administration route. Not available to merchants.
wallet_proof_required 401 You asked whether a wallet is entitled without proving you are entitled to ask. Send the purchase’s client_secret. (A payer wallet-session route also satisfies this; it is used by the hosted wallet page and is not documented for integrators yet.)
wallet_not_signed_in 401 A payer-facing route needs a wallet session. Sign a challenge first.
invalid_wallet_signature 401 The signature does not verify against the address, or the challenge expired or was already used. Request a fresh one; each is single-use.
invalid_client_secret 401 The client_secret does not match this mandate. They are scoped to one purchase and one payer.
Code Status What it means
parameter_missing 400 A required field is absent. param names it.
parameter_invalid 400 Present but unusable: a malformed address, a negative amount, an amount that is not a string of atomic units.
invalid_json 400 The body did not parse.
request_too_large 413 Over 256 KiB. Counted as the bytes arrive, so the connection is closed rather than buffered.
resource_missing 404 No such product, mandate, charge or link, or it is not yours. Tenancy failures look identical to absence on purpose.
unknown_route 404 No such endpoint.
email_taken 409 An account already exists with that email.
acknowledgement_required 400 Signup needs acknowledged: true, recording that you understand Jorvi never holds funds and cannot reverse a settled payment.
Code Status What it means
recurring_unavailable 400 This deployment settles one-time purchases only. Set sellMode: "once". It refuses rather than accepting a subscription it cannot renew.
not_for_sale_once 400 You tried to buy outright something offered only by subscription.
not_for_sale_subscription 400 You tried to subscribe to something sold only outright.
no_payout_wallet 400 Set and prove a payout wallet before taking live payments. There is nowhere for the money to go.
mint_not_allowed 400 That token is not on the platform registry. GET /v1/tokens lists what is accepted, and that route needs authorization: Bearer $KEY.
decimals_unsupported 400 The token’s decimals are outside what the pricing code handles safely.
floor_token 400 The platform’s default token cannot be disabled or removed.
Code Status What it means
transaction_not_issued 400 The signed transaction is not the one we handed you. Only the exact transaction Jorvi issued can pay an order.
broadcast_unavailable 400 No chain is configured behind this deployment, so nothing can be broadcast.
purchases_unavailable 400 One-time purchases need a chain. Same cause.
receipts_unavailable 400 Reading a receipt needs a chain to read it from.
onchain_proof_unavailable 400 On-chain wallet proof is not enabled on this deployment. Prove the wallet by signing the challenge message instead, which needs no chain and no fee.
mandate_not_pending 409 Already activated, or revoked. A mandate is activated once.
already_revoked 409 It was already cancelled.
verification_failed 402 Could not confirm the wallet holds enough to start.
refund_exceeds_charge 400 A refund can never exceed what was received.
refund_failed 400 The chain refused the refund. message carries what it said.
wallet_verification_failed 400 The wallet ownership proof did not check out.
Code Status What it means
rate_limit_exceeded 429 Too many requests. Wait retry-after seconds. ratelimit-limit and ratelimit-remaining are on every response, not only refusals.
request_in_flight 409 The same Idempotency-Key is being processed right now. The key is claimed before the work, so a double-submit cannot run twice. Wait and retry.
internal_error 500 Ours. Safe to retry with backoff; use an Idempotency-Key on creates.
test_mode_only 400 A /v1/test/* helper on a deployment that has none. Production builds do not construct them, so they cannot be switched on by configuration.

Send Idempotency-Key on anything that creates. Keys are scoped to your account, so two merchants both using order-1 never collide.

Replaying a key returns the original response. If the first request is still in flight you get request_in_flight (409) rather than a second execution: the key is claimed before the work starts, not after it finishes.