Errors
Every error is a non-2xx status with the same small JSON shape. Read the code programmatically; show the message to yourself, not your buyer.
The error envelope
errorjson
{
"error": {
"code": "volume_cap_reached",
"message": "the seller has reached their plan's monthly volume"
}
}
The code is a stable, machine-readable string — switch on it. The message is a human-readable hint that may change; don't parse it.
Checkout & payment errors
What you'll see from the publishable-key and secret-key endpoints:
| Code | HTTP | Meaning |
|---|---|---|
missing_key | 401 | No publishableKey in the body. |
publishable_key_required | 401 | A secret key was sent where a publishable one belongs. |
invalid_key | 401 | Key is unknown or revoked. |
unauthorized | 401 | Missing/invalid secret key on a server endpoint. |
origin_required | 400 | No usable Origin/Referer on a live request. |
origin_not_allowed | 403 | Origin is a rejected (blacklisted) domain on the store. |
account_disabled | 403 | Account is suspended or banned. |
email_unverified | 403 | Live key used before the email was verified. |
rate_limited | 429 | Over 120 checkout creates/min from this IP. |
volume_cap_reached | 402 | The account's monthly received-volume ceiling is reached; upgrade to continue. |
no_wallet | 422 | No receiving wallet configured for the chosen network. |
price_not_found | 404 | Unknown priceId/paymentLinkId for this account. |
session_not_found | 404 | Unknown sessionId. |
session_closed | 409 | Session is past its expiry. |
bad_amount | 400 | Session amountCents was not a positive integer. |
bad_request | 400 | Malformed body or an unparseable network/token. |
Payment-status errors
| Code | HTTP | Meaning |
|---|---|---|
payment_not_found | 404 | No payment with that id. |
not_test_mode | 400 | Tried to simulate a live payment. |
not_open | 409 | Tried to simulate a payment that's already settled or closed. |
Dashboard operations (wallets, stores, keys, products…) return the same { error: { code, message } } shape with codes like account_not_found, store_not_found, duplicate_address, duplicate_domain, product_not_found and key_not_found. You meet these through the dashboard UI or when driving the same endpoints with your sk_ key.
Handling errors well
- Branch on
code, not HTTP status alone — several codes share a status (e.g. the 403s). - Don't surface raw errors to buyers. A capped account (
volume_cap_reached) or a setup gap (no_wallet) is yours to fix; show the buyer a calm "can't accept payments right now" if it ever reaches them. The embed already does this. - Retry only the retryable.
429and any5xxare worth a backoff;4xxvalidation errors won't change until you fix the request.