subbydocs
Visit Subby Start building

Errors

Error format, HTTP status codes, every error code and how to fix it, and payment failure codes.

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');
  }
}