When a Subby API request fails, you get a non-2xx status code and an error object. Use code in your application logic. Charge failures on a payment rail use a separate failure-code catalogue and do not mean Subby is a payment engine.
{
"success": false,
"error": {
"type": "invalid_request_error",
"code": "parameter_invalid",
"message": "amount must be a positive integer in kobo. You sent 5000.5.",
"param": "amount",
"request_id": "req_7Tq1LmZ0c",
"doc_url": "https://docs.mysubbyapp.com/errors#parameter_invalid"
}
}
| Field | Description |
|---|---|
type |
The broad category. Use it to decide how to handle the error |
code |
A specific, stable code. Safe to use in your code |
message |
A human-readable explanation. Wording may change, so don't match on it |
param |
The parameter that caused the error, when there is one |
request_id |
Include this when you contact support |
doc_url |
A link to this page, at the entry for the code |
Failed requests (4xx and 5xx) are never metered.
HTTP status codes
| Status | Meaning |
|---|---|
200 OK |
The request worked |
201 Created |
An object was created |
400 Bad Request |
The request was malformed or a parameter was invalid |
401 Unauthorized |
The API key is missing, invalid, revoked, or for the wrong environment |
403 Forbidden |
The key is valid but can't do this |
404 Not Found |
The object or endpoint doesn't exist in this environment |
409 Conflict |
The request conflicts with the object's current state, or an idempotency key was misused |
422 Unprocessable Entity |
The request was valid but can't be completed, such as cancelling an already cancelled subscription |
429 Too Many Requests |
You hit the rate limit |
500, 502, 503, 504 |
Something went wrong on Subby's side. Retry with backoff |
Error types
| Type | What to do |
|---|---|
authentication_error |
Check your API key and base URL. Don't retry until fixed |
permission_error |
Use a key with the right scopes, or request live access |
invalid_request_error |
Fix the request using param and message. Don't retry unchanged |
state_error |
Fetch the object, check its current state, then decide |
idempotency_error |
Use a new key for a new request, or wait and replay |
rate_limit_error |
Wait for Retry-After, then retry |
api_error |
Retry with exponential backoff and the same Idempotency-Key |
Error codes
api_key_missing
HTTP 401 · authentication_error
No API key was provided.
Send your key in the Authorization header as Bearer sk_test_... or Bearer sk_live_....
api_key_invalid
HTTP 401 · authentication_error
The API key provided is not valid.
Copy the key again from Dashboard → Developers → API keys. Check for extra spaces or a truncated value.
key_revoked
HTTP 401 · authentication_error
This API key has been revoked or has expired after a roll.
Create or roll a key in the dashboard and deploy the new value.
key_environment_mismatch
HTTP 401 · authentication_error
The key's environment does not match the base URL.
Use sk_test_/rk_test_ keys with the sandbox base URL and sk_live_/rk_live_ keys with the live base URL.
live_access_not_enabled
HTTP 403 · permission_error
Your account does not have live API access yet.
Request live access from Dashboard → Developers. Keep building in sandbox while your request is reviewed.
account_suspended
HTTP 403 · permission_error
API access for this account is suspended.
Contact support. Your sandbox keeps working.
insufficient_scope
HTTP 403 · permission_error
This restricted key does not have the scope required for this request.
Add the scope named in the message to the restricted key, or use a different key.
parameter_missing
HTTP 400 · invalid_request_error
A required parameter is missing.
Add the parameter named in param.
parameter_invalid
HTTP 400 · invalid_request_error
A parameter has an invalid value or type.
Check param and message. Amounts must be positive integers in kobo; timestamps must be ISO 8601 UTC.
parameter_unknown
HTTP 400 · invalid_request_error
The request contains a parameter this endpoint does not accept.
Remove the parameter named in param, or check the spelling against the API reference.
invalid_json
HTTP 400 · invalid_request_error
The request body is not valid JSON.
Send a valid JSON body with Content-Type: application/json.
resource_missing
HTTP 404 · invalid_request_error
No object exists with this ID in this environment.
Check the ID, and check that you are using the same environment the object was created in.
route_not_found
HTTP 404 · invalid_request_error
This endpoint does not exist.
Check the path and method against the API reference. /test/* endpoints only exist in sandbox.
currency_not_supported
HTTP 400 · invalid_request_error
This currency is not supported.
Use NGN.
plan_archived
HTTP 422 · state_error
The plan is archived and cannot be used for new subscriptions.
Use an active plan, or create a new plan.
customer_has_no_payment_method
HTTP 422 · state_error
The customer has no payment method to charge.
Collect a payment method with a checkout session in setup or subscription mode first.
subscription_not_pausable
HTTP 422 · state_error
The subscription cannot be paused in its current state or its plan does not allow pausing.
Only active and past_due subscriptions on plans with pause_enabled: true can be paused.
subscription_not_paused
HTTP 422 · state_error
The subscription is not paused.
Fetch the subscription and check status before resuming.
subscription_already_cancelled
HTTP 422 · state_error
The subscription is already cancelled.
Create a new subscription if the customer wants to resubscribe.
charge_not_retryable
HTTP 422 · state_error
This charge cannot be retried.
Only failed charges with a retryable failure code can be retried. Ask the customer for a new payment method instead.
checkout_session_expired
HTTP 422 · state_error
The checkout session has expired.
Create a new checkout session.
nudge_recipient_missing
HTTP 422 · state_error
The customer has no contact detail for the requested channel.
Add a phone number for SMS or WhatsApp, or an email for email, or pick another channel.
nudge_rate_limited
HTTP 422 · state_error
Too many nudges were sent to this customer recently.
Subby allows at most 3 manual nudges per customer per 24 hours to protect deliverability. Try again later.
webhook_url_invalid
HTTP 400 · invalid_request_error
The webhook URL is not a valid public HTTPS URL.
Use an https:// URL that is reachable from the internet. Private IP ranges and localhost are not allowed.
idempotency_key_reused
HTTP 409 · idempotency_error
This idempotency key was already used with a different request body.
Use a new idempotency key for a different request.
idempotency_request_in_progress
HTTP 409 · idempotency_error
A request with this idempotency key is still being processed.
Wait a moment and replay the request with the same key.
rate_limited
HTTP 429 · rate_limit_error
Too many requests.
Wait for the number of seconds in Retry-After, then retry. See Rate limits.
test_helper_live_mode
HTTP 404 · invalid_request_error
Test helpers are only available in sandbox.
Send /test/* requests to the sandbox base URL with a sandbox key.
internal_error
HTTP 500 · api_error
Something went wrong on Subby's side.
Retry with exponential backoff and the same Idempotency-Key. If it continues, contact support with the request_id.
service_unavailable
HTTP 503 · api_error
The service is temporarily unavailable.
Retry with exponential backoff. Check the status page.
Payment failure codes
A declined payment is not an API error. The API request that created or retried the charge succeeds, and the charge object has status: "failed" with a failure_code. You'll also receive a charge.failed webhook.
insufficient_funds
The account did not have enough funds.
do_not_honor
The issuer declined without a specific reason.
issuer_unavailable
The issuing bank could not be reached.
processing_error
A temporary error on the payment rail that collects the charge.
transaction_limit_exceeded
The payment exceeded a daily or per-transaction limit.
expired_card
The card has expired. The customer must add a new payment method.
incorrect_details
Card number, expiry, CVV or account details were wrong.
authentication_required
The payment needs customer authentication (OTP/3-D Secure). Subby sends the customer a link to authorise it.
card_reported_stolen
The card was reported lost or stolen. Do not show this reason to the customer.
authorization_revoked
The customer or bank revoked the mandate or card authorisation.
Subby uses the failure code to decide whether retrying makes sense. For example, insufficient_funds is retried on your plan's schedule, but card_reported_stolen is not retried and the customer is asked to use a different payment method.
Handling errors in code
const res = await fetch(`${process.env.SUBBY_BASE_URL}/subscriptions`, options);
const body = await res.json();
if (!body.success) {
const { type, code, param, request_id } = body.error;
switch (type) {
case 'rate_limit_error':
// wait for Retry-After, then retry with the same Idempotency-Key
break;
case 'api_error':
// retry with backoff and the same Idempotency-Key
break;
case 'invalid_request_error':
logger.warn({ code, param, request_id }, 'Fix the request before retrying');
break;
default:
logger.error({ type, code, request_id }, 'Subby request failed');
}
}