Getting started
Errors
Status codes, error codes, and field errors, and how to handle each.
A failed request returns a 4xx or 5xx status and a JSON body. The body has a code that stays the same and an error sentence for developers. Branch on the status and the code. Don't parse error, since its wording can change.
{
"code": "forbidden",
"error": "not authorized for this app"
}Field errors
When a request fails because of its input, the body also has an errors array, with one item per field. A failed validation returns 422 with code invalid and lists every field that failed.
{
"code": "invalid",
"error": "email is required; phone_number is required",
"errors": [
{ "field": "email", "code": "invalid", "error": "email is required" },
{ "field": "phone_number", "code": "required", "error": "phone_number is required" }
]
}Each item's code is one of these:
required: the field is missing or blank.invalid: the value isn't allowed, such as an amount of0or an unknown status.currency_unsupported: the currency isn'tXAForXOF.phone_invalid: the phone number has no country code, or the wrong number of digits for its country.unchanged: the value is the one already set, such as a new price amount equal to the current one.too_soon: the date is too close, such as a price change less than 14 days ahead.unknown_field: an update sent a field the endpoint doesn't take.not_found: the ID in the field doesn't exist in the app, such as aprice_idor aninvoice_idwhen you create a session.url_invalid: the URL isn't an absolute URL with a host, or useshttpwhere onlyhttpsis allowed.
A product's slug must be unique in its app. The slug comes from the name unless you send one. A clash returns 409 with code name_taken, and its errors item names the name field.
Error codes
These are the codes a request with a secret key can get. Each code shows the HTTP status it comes with.
Error codes
invalid_json400The body isn't valid JSON, has a value of the wrong type, or has a field the endpoint doesn't take.invalid422One or more fields failed validation. Seeerrorsfor each field. AnIdempotency-Keyheader longer than 255 characters gets this code with400.payment_declined402The payment failed. The provider can also give a more specific reason, listed below.insufficient_funds402The customer's balance is too low.payment_not_approved402The customer declined the prompt on their phone or let it time out.wallet_not_found402The number has no wallet on the network charged, or isn't a valid number.wallet_limit_reached402The customer's wallet reached a transaction or balance limit.wallet_busy402Another payment is already waiting for the customer's approval.network_unavailable402The network can't take this payment now, or can't take it at all on your provider account.forbidden403The key is missing, revoked, or for another app. A secret key also gets this on endpoints that need a dashboard sign-in, and a dashboard sign-in gets it on endpoints that need a secret key, like sessions.email_unverified403The app is in Live mode and the team owner hasn't verified their email.plan_limit403The request would pass a limit on your plan, such as its number of Live subscribers.plan_read_only403Your plan has made this Live app read-only. Reads still work, and so does charging an entry.not_found404The app, or the object named in the path or body, doesn't exist.plan_offer_not_found404The plan change link is wrong. Send the customer a new one with Change plan.name_taken409Another product in the app already has this slug.already_paid409The entry is already paid, or you asked to pay an invoice that is already paid.invoice_not_open409The invoice is void, for example because the subscription was canceled. You can't create a session to pay it.subscription_canceled409The subscription has ended, so a scheduled cancel can't be undone and you can't request a cancel. Create a new subscription instead.subscription_not_active409Only an active subscription can be canceled. You can't request a cancel for one that isn't, for example one whose first period was never paid.cancel_already_scheduled409The subscription is already scheduled to cancel, so you can't request a cancel.payment_in_progress409A payment for this entry is already waiting for approval. A plan change or cancel that would void an unpaid period, or replace an upgrade waiting for payment, also gets this while that payment is in flight.plan_change_unavailable409The subscription isn't active, so its plan can't change. A subscription whose first period was never paid can't change plan.plan_offer_inactive409The plan change offer was already accepted, canceled, or replaced by a newer one.session_not_open409The session is already completed or expired, so it can't be expired.idempotency_in_progress409A request with the same Idempotency-Key is still running. Retry after a moment.no_pending_change409The price has no scheduled change to cancel.change_in_effect409The price's pending change has reached its day, so it can't be replaced or canceled.conflict409The request conflicts with the current state of the object.price_archived410The price is archived, so no new subscription can use it.plan_offer_expired410The plan change offer ran out. It lasts 7 days, or until the current period ends if that comes first.session_expired410The session expired before the customer submitted their details. The hosted checkout page gets this, not your server. Create a new session.price_not_eligible422The subscription can't move to this price: it is the current price, archived, for another product, in another currency, or not in the app.not_accepting_payments422The Live app can't take payments yet: the team owner hasn't verified their email, or no payment provider is connected. An upgrade with something to pay gets this too.payment_unavailable422The payment method isn't offered, or doesn't take payments in the invoice's currency.idempotency_key_reused422The Idempotency-Key was already used with a different request in the last 24 hours. Use a new key for a new request.webhook_url_missing422You asked to send or resend a webhook, but the app has no webhook URL.rate_limited429Too many test webhooks. Each app can send 10 a minute.internal500Something went wrong on Abuna's side. Retry later.
Rate limits
Each IP address can make 600 requests a minute. Past that, the API returns 429 with a plain-text body, not JSON. Wait for the number of seconds in the Retry-After header, then try again.
Next steps
- Authentication and API keysAuthenticate requests with your secret key, and know which key goes where.
- Lists and filtersWhat list endpoints return, how to filter them, and how many rows they send.
- SubscriptionsStart, list, read, update, cancel, keep, and change the plan of a customer's subscription to a price.