Skip to content

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.

initialize
{ "error": "Unauthorized API Key" }
initialize, validation failure
{
  "error": "Invalid request body",
  "details": [
    "amount must be a positive number with at most 2 decimal places"
  ]
}
verify and submit
{
  "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

StatuserrorCause
401Missing API KeyNo x-api-key header on the request.
401Unauthorized API KeyThe key is unknown, revoked, or its account no longer exists.
400Invalid JSON bodyThe request body did not parse as JSON.
400Invalid request bodyOne or more fields failed validation. Each reason is listed in details.
400URL policy messageA 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.
403Vendor account is not active to process paymentsThe account exists but its status is not active. KYC is not approved yet, or it was suspended.
429The vendor daily transaction limit has been reached. Please try again tomorrow.Your own daily limit is used up.
429The platform daily transaction limit has been reached. Please try again tomorrow.The platform-wide daily limit is used up.
503Checkout is temporarily unavailable for maintenance. Please try again shortly.Maintenance mode is on. Retry shortly.
500Internal server errorSomething 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.

StatuserrorCause
401Missing API KeyNo x-api-key header.
401Unauthorized API KeyUnknown or revoked key.
400Missing required matching fieldsOne of sessionId, trxId or provider is missing or is not a string.
404Invalid checkout sessionsessionId is not a UUID.
400Invalid trxId formatAfter trimming and upper-casing, the TrxID does not match [A-Z0-9]{6,20}.
400Invalid providerNot one of bkash, nagad, rocket, upay, cellfin.
500Internal system errorUnhandled 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

Statuserrormessage
409ALREADY_COMPLETEDThis session was already completed with a different transaction.
400VERIFICATION_FAILEDCould not verify transaction. Please ensure the TrxID and amount match perfectly.
429TOO_MANY_ATTEMPTSToo many failed attempts on this session. Please contact the merchant.
410SESSION_EXPIREDThis checkout session has expired.
410SESSION_UNAVAILABLEThis checkout session is no longer available.
404NOT_FOUNDInvalid checkout session.
403VENDOR_INACTIVEThis vendor account is not active.
202PENDING_VERIFICATIONPayment 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 } }.

StatusCodeMessage and meaning
429RATE_LIMITEDToo many requests. Please slow down. Limits are per API key and per endpoint. Wait a minute, then retry with backoff.
402PLATFORM_FEE_DUEThe 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.
403noneThis 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, 503 and a 429 from a daily limit are worth retrying. Initialize is idempotent on order_id, so a retry cannot create a second session.
  • 400, 401, 403 and 404 will not change on their own. Fix the request or the account first.
  • TOO_MANY_ATTEMPTS locks that session. Open a new one rather than retrying the old.