SUQODocs
User GuideAPI ReferenceSDKs

Errors

Error shapes that apply across the API, the auth failures every endpoint can return, and the per-endpoint 400s in one place.

Trailing slash required

Every route ends in / (e.g. /api/v1/checkout-sessions/).

  • A GET without the slash 301-redirects to the slashed URL.
  • A POST / write without the slash fails — the redirect can't preserve the request body.

Always include the trailing slash.

Error format

Field-specific validation errors return an object keyed by field name, with an array (or string) message:

{
  "field_name": ["Error message."]
}

General errors (auth failures, not found) return a detail key:

{
  "detail": "Error message."
}

A few endpoints that take no request body answer with a bare list instead — there is no field to key the message on. Cancel and resume are the two.

Authentication and KYC

Returned by every endpoint. See Authenticate your requests.

StatusTriggerBody
401Key missing, invalid, expired, or the account behind it deactivated{"detail": "Invalid API key."}
403Owner not KYC-verified{"status_code": "KYC_INCOMPLETE" | "KYC_VERIFICATION_PENDING", "message": "KYC verification needed to perform this action."}
403Key not usable for this account{"detail": "You do not have permission to perform this action."}

The 401 detail distinguishes the cases: Authentication credentials were not provided. (no Authorization header, or one that does not start with Bearer ), Invalid API key. (no key matches), User inactive or deleted. (the account behind the key is deactivated — checked before expiry, so a valid key on a dead account says so), and API key has expired.

The two 403s are different shapes, so branch on the presence of detail, not on the status code. The KYC body has status_code and message and no detail; the other has detail alone. KYC is the one a new partner meets, and it is cleared in the dashboard rather than here.

GET /api/v1/products/ and every /webhooks/ route are the exceptions to the KYC 403: they work before KYC verification, so only the second shape occurs on them.

KYC is not enforced on the sandbox. An unverified account can exercise every endpoint there — see Test against the sandbox.

Rate limits

Only Create a checkout session is rate-limited. Every other endpoint is unlimited.

StatusTriggerBody
429More than 20 sessions a minute on this account{"detail": "Request was throttled. Expected available in 41 seconds."}

The wait is named inside the detail string, not in a separate field. Retry after it.

Creating a checkout session

From Create a checkout session.

StatusTriggerBody
400return_url missing, null or blank{"return_url": ["A return URL is required."]}
400items empty, or more than tenKeyed on items
400An item's pbp_id unknown / not visibleKeyed on items
400customer_id not a customer on your accountKeyed on customer_id

Two items resolving to the same product is not an error — the last one is kept and the other is dropped silently. See Items.

Adding or updating a customer

From Customers.

StatusTriggerBody
400Phone is not a valid Nepali mobile numberKeyed on phone
400Phone sent at a value other than its current one{"phone": ["Phone cannot be changed."]}
400Adding your own number as a customer{"phone": ["You cannot add yourself as a customer."]}

A phone you already hold is not an error: the customer is corrected and the response is 200 instead of 201.

Creating a subscription

From Create a subscription.

StatusTriggerBody
400pbp_id unknown / not visible for this seller{"pbp_id": "PlanBillingPeriod with public_id '...' does not exist or is not visible."}
400Account has no API usage type{"api_usage_type": "Your account does not have an API usage type configured."}
400return_url missing{"return_url": "A return URL is required for reminder-and-payment accounts."}
400Client already holds a live subscription on another billing period of the same product["You already have a subscription to this product."] — a bare list, not a keyed object

Managing a subscription

From Cancel or reschedule.

StatusTriggerBody
400Date in the past{"next_billing_cycle": ["next_billing_cycle must be in the future or today."]}
400Subscription not found / not owned{"subscription_id": "Subscription '...' does not exist."}
400One-time purchase — nothing to cancel, resume or reschedule["This is a one-time purchase, so there is nothing to cancel."]

On this page