Errors
Two response shapes. The checkout-session endpoint returns a plain error string. The two claim endpoints return a machine-readable error code alongside a message written for a buyer to read.
{ "error": "Unauthorized API Key" }{
"error": "Invalid request body",
"details": [
"amount must be a positive number with at most 2 decimal places"
]
}{
"success": false,
"error": "VERIFICATION_FAILED",
"message": "Could not verify transaction. Please ensure the TrxID and amount match perfectly."
}A 500 never carries detail.Validation failures explain themselves in
details. A server-side failure returns only Internal server error or Internal system error, because the caller does not get to see internals.POST /api/v1/checkout/initialize
| Status | error | Cause |
|---|---|---|
| 401 | Missing API Key | No x-api-key header on the request. |
| 401 | Unauthorized API Key | The key is unknown, revoked, or its account no longer exists. |
| 400 | Invalid JSON body | The request body did not parse as JSON. |
| 400 | Invalid request body | One or more fields failed validation. Each reason is listed in details. |
| 400 | URL policy message | A success_url, cancel_url or webhook_url broke the URL rules. The message is specific: the URL is missing or exceeds 2048 characters, the URL is malformed, the URL must not contain embedded credentials, the URL must use https:, or the host is not in this account’s allowed-host list. |
| 403 | Vendor account is not active to process payments | The account exists but its status is not active. KYC is not approved yet, or it was suspended. |
| 429 | The vendor daily transaction limit has been reached. Please try again tomorrow. | Your own daily limit is used up. |
| 429 | The platform daily transaction limit has been reached. Please try again tomorrow. | The platform-wide daily limit is used up. |
| 503 | Checkout is temporarily unavailable for maintenance. Please try again shortly. | Maintenance mode is on. Retry shortly. |
| 500 | Internal server error | Something failed on our side. Safe to retry with the same order_id. |
POST /api/v1/checkout/verify
The API-key-authenticated claim. It returns everything below, plus the shared claim outcomes in the next table.
| Status | error | Cause |
|---|---|---|
| 401 | Missing API Key | No x-api-key header. |
| 401 | Unauthorized API Key | Unknown or revoked key. |
| 400 | Missing required matching fields | One of sessionId, trxId or provider is missing or is not a string. |
| 404 | Invalid checkout session | sessionId is not a UUID. |
| 400 | Invalid trxId format | After trimming and upper-casing, the TrxID does not match [A-Z0-9]{6,20}. |
| 400 | Invalid provider | Not one of bkash, nagad, rocket, upay, cellfin. |
| 500 | Internal system error | Unhandled failure while claiming. |
POST /api/v1/checkout/submit
The buyer-facing claim used by the hosted page. Identical to verify without the two API-key rows: it returns Missing required matching fields, Invalid checkout session, Invalid trxId format, Invalid provider, Internal system error and the shared claim outcomes below.
Claim outcomes, shared by verify and submit
| Status | error | message |
|---|---|---|
| 409 | ALREADY_COMPLETED | This session was already completed with a different transaction. |
| 400 | VERIFICATION_FAILED | Could not verify transaction. Please ensure the TrxID and amount match perfectly. |
| 429 | TOO_MANY_ATTEMPTS | Too many failed attempts on this session. Please contact the merchant. |
| 410 | SESSION_EXPIRED | This checkout session has expired. |
| 410 | SESSION_UNAVAILABLE | This checkout session is no longer available. |
| 404 | NOT_FOUND | Invalid checkout session. |
| 403 | VENDOR_INACTIVE | This vendor account is not active. |
| 202 | PENDING_VERIFICATION | Payment found. The merchant is confirming it. Only returned when the vendor has turned on balance verification and the matching SMS has not yet confirmed against the wallet’s running balance. Not an error: success is false only because the claim is not final yet, and this attempt is not counted against TOO_MANY_ATTEMPTS. |
Limits and account state
Returned by any authenticated endpoint, in the shape { error: { code, message } }.
| Status | Code | Message and meaning |
|---|---|---|
| 429 | RATE_LIMITED | Too many requests. Please slow down. Limits are per API key and per endpoint. Wait a minute, then retry with backoff. |
| 402 | PLATFORM_FEE_DUE | The merchant has unpaid platform fees above their limit, so no new session can be opened. Sessions already pending still complete. The merchant clears it from Billing in the portal. |
| 403 | none | This brand has been archived and can no longer accept new payments. Returned by initialize in the legacy shape { error: "..." }. Use a key that belongs to an active brand. |
Retrying
500,503and a429from a daily limit are worth retrying. Initialize is idempotent onorder_id, so a retry cannot create a second session.400,401,403and404will not change on their own. Fix the request or the account first.TOO_MANY_ATTEMPTSlocks that session. Open a new one rather than retrying the old.
NextGoing live →