Search pages in the SMS Pay documentation.
Statuses tell you where a payment, SMS event, or webhook delivery is in the lifecycle. Your integration should use these values exactly and treat unknown future values conservatively.
payment.paid webhook or retrieve the intent from your backend.PAID.REVIEW_REQUIRED, FAILED, and EXPIRED.| Status | Meaning | Merchant action |
|---|---|---|
PENDING | Waiting for matching SMS or manual confirmation. | Do not fulfill. |
PAID | Payment confirmed. | Fulfill idempotently. |
FAILED | Payment failed due to processing or business rule failure. | Review or ask customer to retry. |
EXPIRED | Intent expired before valid confirmation. | Do not fulfill automatically. |
REJECTED | Manual review rejected the payment. | Do not fulfill. |
REVIEW_REQUIRED | Partial or ambiguous match needs manual review. | Inspect reconciliation dashboard. |
| Status | Meaning |
|---|---|
RECEIVED | SMS event stored and queued. |
PARSED | SMS parsed successfully. |
UNPARSED | SMS did not match supported provider format. |
MATCH_PENDING | Parsed but no confirmed intent yet. |
MATCHED | Linked to a payment intent. |
FAILED | Failed due to duplicate, disallowed method, or other rule. |
MANUALLY_RESOLVED | Resolved by an admin. |
REJECTED | Rejected by an admin. |
| Status | Meaning |
|---|---|
PENDING | Waiting for match or review. |
MATCHED | Confirmed match. |
REVIEW_REQUIRED | Needs admin review. |
FAILED | Matching failed. |
EXPIRED | Match window expired. |
UNPARSED | SMS could not be parsed. |
DUPLICATE | Duplicate transaction ID or message. |
MANUALLY_RESOLVED | Admin resolved it. |
REJECTED | Admin rejected it. |
| Status | Meaning |
|---|---|
PENDING | Delivery is queued. |
DELIVERED | Endpoint returned 2xx. |
RETRYING | Delivery failed and will retry. |
FAILED | Delivery failed permanently. |
function canFulfill(status: string) {
return status === "PAID";
}
function needsOperator(status: string) {
return (
status === "REVIEW_REQUIRED" || status === "FAILED" || status === "EXPIRED"
);
}
Expired payment response:
{
"id": "b5012f33-207e-4999-bf8d-5a1ebb10988e",
"status": "EXPIRED",
"amount": "500",
"currency": "BDT",
"customerReference": "ORDER-10045",
"receiverMsisdn": "01700000001"
}
Validation error example:
{
"statusCode": 400,
"message": ["receiverMsisdn must be a valid Bangladeshi mobile number"],
"error": "Bad Request"
}
Business error example:
{
"code": "INTENT_EXPIRED",
"message": "Payment intent has expired"
}
| Error | Cause | Fix |
|---|---|---|
401 Unauthorized | Missing or invalid API key/signature. | Check X-Api-Key and X-Signature. |
403 Forbidden | Wrong environment or missing permission. | Use sandbox key for sandbox endpoints, live key for live flows. |
404 Not Found | Intent or endpoint ID does not exist in this merchant/environment. | Check ID and API key environment. |
INTENT_EXPIRED | Payment intent is past expiry. | Create a new checkout/payment intent. |
duplicate_trx_id | SMS transaction ID was already used. | Do not reuse the SMS event; investigate manually. |
RECEIVER_ACCOUNT_NOT_CONFIGURED | No active receiver account for the merchant + environment + payment method. | Configure receiver accounts in the dashboard before creating live intents. |
successUrl/failedUrl/cancelUrl/expiredUrl validation error | URL uses an unsafe protocol or is malformed. | Use only absolute http or https URLs. javascript:, data:, file: are rejected. |
When redirect URLs are provided on a payment intent, the hosted checkout page automatically redirects the customer after resolution:
| Outcome | URL used |
|---|---|
PAID | successUrl |
FAILED or REJECTED | failedUrl |
EXPIRED | expiredUrl |
| Customer cancels | cancelUrl |
The redirect includes a 3-second delay with a visible countdown message. These query parameters are appended:
| Parameter | Value |
|---|---|
payment_intent_id | Intent UUID |
status | Final status (e.g. PAID, EXPIRED, CANCELLED) |
customer_reference | The reference you provided at intent creation |
Warning: Do not fulfill orders solely based on browser redirects. Always verify status server-side using webhooks or
GET /v1/payments/intents/:id.
REVIEW_REQUIRED is not paid.EXPIRED can happen when checkout is loaded after expiry or by expiry worker.