Reference
API reference
Three calls and one postback. Base URL https://api.antpay.network. JSON in, JSON out.
| Call | What it does |
|---|---|
POST /api/v1/checkouts | Create a hosted checkout |
GET /api/v1/checkouts/{checkoutId} | Read a checkout by id |
GET /api/v1/transactions/{reference} | Read a payment by your reference |
POST to your postback URL | The payment result, sent by AntPay to you |
Prefer a machine-readable file? Download the OpenAPI description and import it into your API client.
Create a hosted checkout
Creates a hosted payment link for one order. Call it from your server only: your API key must never reach a browser or an app. Then send the customer to checkoutUrl. The AntPay page collects the payer's details, makes the QR and shows the result.
- Returns
200withcheckoutUrl,checkoutIdandexpiresAt. The link ishttps://pay.antpay.network/qr/<checkoutId>. expiresAtis when the link stops accepting customer details. It is not the payment deadline.- This response does not include
transactionId. You get it in the postback, or astxnIdfromGET /api/v1/checkouts/{checkoutId}. - When a call fails with
4xx, no payment is created. The only exception is a duplicatereference, which already exists. On a500or no answer, checkGET /api/v1/transactions/{reference}before you retry. - The IP allow-list applies to every call, and request signing if it is switched on for your account.
- Optional
accountNumber+walletName: send both and the page opens straight on the QR code; leave both out and it opens on the form. - Whole rupees only:
1500, not1500.50. A decimal amount returns400DECIMAL_VALUES_NOT_SUPPORTED.
Headers
| Header | Value |
|---|---|
X-Api-Key required | Your payment key. |
Content-Type required | application/json |
X-Timestamp, X-Signature | Only when request signing is on for your account. See Request signing. |
Request body
| Field | Type | Description |
|---|---|---|
reference required | string | Your order id: 1 to 120 characters from letters, digits, |
amount required | number | Amount in PKR, whole rupees only, minimum PKR 300 (below that: |
paymentMethod required | "qr" |
|
paymentType required | "onetime" | Always |
returnUrl required | string (uri) | Where the page sends the customer after a final result. Use https. We add no query parameters, so your landing page must check the status itself. |
successUrl | string (uri) | Optional. Used as the postback URL only when no postback URL is set on your API key or your account. |
expiresIn | number | Minutes the customer has to submit their details. Default 15. Values outside 1 to 1380 (23 h) are clamped to that range, not rejected. A missing value, a non-number or 0 becomes 15. |
user | object | Free-form metadata. It is returned to you in the webhook field |
accountNumber | string or null | Optional — send together with |
walletName | "easypaisa" "jazzcash" "sadapay" "nayapay" or null | Optional — send together with |
Request examples
With accountNumber + walletName — the page opens straight on the QR
{
"reference": "ORD-10021",
"amount": 1500,
"paymentMethod": "qr",
"paymentType": "onetime",
"returnUrl": "https://shop.example.com/orders/ORD-10021",
"expiresIn": 15,
"user": {
"customerId": "C-778"
},
"accountNumber": "03121234567",
"walletName": "easypaisa"
}Without them — the customer picks the wallet and enters the details
{
"reference": "ORD-10021",
"amount": 1500,
"paymentMethod": "qr",
"paymentType": "onetime",
"returnUrl": "https://shop.example.com/orders/ORD-10021",
"expiresIn": 15,
"user": {
"customerId": "C-778"
}
}
Response fields (200)
| Field | Type | Description |
|---|---|---|
success | boolean | |
checkoutUrl | string (uri) | Send the customer here, exactly as returned: |
checkoutId | string (uuid) | Store it. It identifies the webhook ( |
expiresAt | string (date-time) | When the link stops accepting customer details (now + |
Responses
200
Created — send the customer to checkoutUrl. The payment is pending.
{
"success": true,
"checkoutUrl": "https://pay.antpay.network/qr/3f6c1d2e-8a4b-4c1e-9d7a-2b5e6f7a8c90",
"checkoutId": "3f6c1d2e-8a4b-4c1e-9d7a-2b5e6f7a8c90",
"expiresAt": "2026-11-01T10:15:00.000Z"
}400
Validation failed (errors), or the reference was used before for this API key (error). A duplicate carries no link: get the checkoutId from GET /transactions/{reference} and rebuild the link. A qr amount that is not whole rupees returns the single DECIMAL_VALUES_NOT_SUPPORTED body instead.
Amount below the QR minimum (PKR 300)
{
"success": false,
"statusCode": "AMOUNT_NOT_SUPPORTED",
"error": "Amount not supported. Minimum is PKR 300."
}validation
{
"success": false,
"errors": [
"paymentType only supports \"onetime\"",
"returnUrl must be a valid URL"
]
}Amount not whole rupees (e.g. 1500.50)
{
"success": false,
"statusCode": "DECIMAL_VALUES_NOT_SUPPORTED",
"error": "decimal-values-are-not-supported"
}Only one of accountNumber / walletName sent
{
"success": false,
"errors": [
"accountNumber and walletName must be passed together"
]
}walletName not allowed
{
"success": false,
"errors": [
"walletName must be one of easypaisa, jazzcash, sadapay, nayapay"
]
}accountNumber not a mobile wallet number
{
"success": false,
"errors": [
"accountNumber must be a mobile wallet number, e.g. 03001234567"
]
}missing
{
"success": false,
"errors": [
"reference is required",
"amount is required",
"paymentType is required",
"returnUrl is required"
]
}duplicate
{
"success": false,
"error": "Duplicate reference. Transaction with this reference already exists for this API key."
}401
Missing or invalid API key, or a key not attached to a merchant. When request signing is on for your account, a signing error also returns 401. Nothing created.
missing
{
"success": false,
"error": "Missing X-Api-Key header"
}invalid
{
"success": false,
"error": "Invalid API key"
}noMerchant
{
"success": false,
"error": "API key has no merchant association"
}Request signing is on and the signature failed
{
"success": false,
"error": {
"code": "SIGNATURE_INVALID",
"message": "Request signature rejected (bad_signature)."
}
}403
Your account is not active, or the calling IP is not on your allow-list. Nothing created.
inactive
{
"success": false,
"error": "MERCHANT_INACTIVE",
"message": "This merchant account is not active. Contact support."
}noAllowlist
{
"success": false,
"error": "IP_ALLOWLIST_NOT_CONFIGURED",
"message": "This merchant has no IP allowlist configured on production. Contact your account manager to add your server IPs."
}ipNotAllowed
{
"success": false,
"error": "IP_NOT_ALLOWED",
"message": "Your IP is not on this merchant's allowlist. Contact your account manager to add it."
}422
Your account is not set up for QR yet. Nothing created — contact your account manager; your request is fine.
noRate
{
"success": false,
"status": "failed",
"statusCode": "NO_RATE_CONFIGURED",
"message": "QR is not fully configured for this merchant"
}notEnabled
{
"success": false,
"status": "failed",
"statusCode": "NO_PSP_ROUTE",
"error": "No QR payment processor configured for this merchant"
}429
Rate limit exceeded. Back off for Retry-After seconds.
{
"success": false,
"error": "Too Many Requests",
"message": "Rate limit exceeded: 300 requests per minute",
"retryAfter": 12
}500
The checkout may or may not exist. Check GET /transactions/{reference} before you retry.
{
"success": false,
"error": "Failed to create checkout session"
}Get a checkout
Returns one of your checkouts by checkoutId, including our txnId (the transactionId in postbacks). A checkout of another merchant is simply not found. IP allow-listed.
If you sent accountNumber + walletName when you created it, both come back here (the number masked, e.g. 0312•••4567); otherwise both are null.
Treat the postback as the primary signal; poll as a fallback only, no more often than every 10 s per payment.
Headers
| Header | Value |
|---|---|
X-Api-Key required | Your payment key. |
X-Timestamp, X-Signature | Only when request signing is on for your account. See Request signing. |
Path
| Parameter | Type | Description |
|---|---|---|
checkoutId required | string (uuid), in the path | The |
Response fields (200)
| Field | Type | Description |
|---|---|---|
success | boolean | |
checkout | object |
checkout object
| Field | Type | Description |
|---|---|---|
checkoutId | string (uuid) | |
txnId | string or null | Our |
reference | string | |
amount | number | PKR. On QR payments, the payable amount once a QR exists. |
paymentMethod | string | |
status | "pending" "completed" "failed" | |
createdAt | string (date-time) | |
walletName | "easypaisa" "jazzcash" "sadapay" "nayapay" or null | The |
accountNumber | string or null | The |
Responses
200
The checkout.
{
"success": true,
"checkout": {
"checkoutId": "3f6c1d2e-8a4b-4c1e-9d7a-2b5e6f7a8c90",
"txnId": "FDX-1A2B3C4D",
"reference": "ORD-10021",
"amount": 1500,
"paymentMethod": "qr",
"status": "pending",
"createdAt": "2026-11-01T10:00:00.000Z",
"walletName": "easypaisa",
"accountNumber": "0312•••4567"
}
}401
Missing or invalid API key, or a key not attached to a merchant (or a signing error when signing is on).
Key problem
{
"success": false,
"error": "API key has no merchant association"
}Request signing is on and the signature failed
{
"success": false,
"error": {
"code": "SIGNATURE_INVALID",
"message": "Request signature rejected (bad_signature)."
}
}403
The calling IP is not on your allow-list.
{
"success": false,
"error": "IP_NOT_ALLOWED",
"message": "Your IP is not on this merchant's allowlist. Contact your account manager to add it."
}404
No checkout with that id for your merchant. This is the API's own answer. A 404 whose body is {"success":false,"error":"NOT_FOUND"} means the address was refused before it reached the API (a wrong path, or a reference or id with characters that cannot travel in a URL): it says nothing about whether the payment exists.
{
"success": false,
"error": "Checkout session not found"
}429
Rate limit exceeded. Back off for Retry-After seconds.
500
Internal error — retry later.
{
"success": false,
"error": "Failed to retrieve checkout session"
}Get a payment by your reference
Returns the payment for one of your references, with its checkoutId and our txnId. Use it after an unclear create (timeout or 5xx), or to rebuild a hosted link as https://pay.antpay.network/qr/<checkoutId>. The lookup is per merchant: if you use more than one API key, keep references unique across all of them. IP allow-listed.
statusCode is null while the payment is pending.
Headers
| Header | Value |
|---|---|
X-Api-Key required | Your payment key. |
X-Timestamp, X-Signature | Only when request signing is on for your account. See Request signing. |
Path
| Parameter | Type | Description |
|---|---|---|
reference required | string, in the path | Your order |
Response fields (200)
| Field | Type | Description |
|---|---|---|
success | boolean | |
transaction | object |
transaction object
| Field | Type | Description |
|---|---|---|
reference | string | |
txnId | string or null | Our |
checkoutId | string (uuid) | Rebuild a hosted link as |
amount | number | PKR. On QR payments, the payable amount once a QR exists. |
paymentMethod | string | |
status | "pending" "completed" "failed" | |
statusCode | string or null |
|
createdAt | string (date-time) | |
updatedAt | string (date-time) | |
walletName | "easypaisa" "jazzcash" "sadapay" "nayapay" or null | The |
accountNumber | string or null | The |
Responses
200
The payment.
{
"success": true,
"transaction": {
"reference": "ORD-10021",
"txnId": "FDX-1A2B3C4D",
"checkoutId": "3f6c1d2e-8a4b-4c1e-9d7a-2b5e6f7a8c90",
"amount": 1500,
"paymentMethod": "qr",
"status": "completed",
"statusCode": "SUCCESS",
"createdAt": "2026-11-01T10:00:00.000Z",
"updatedAt": "2026-11-01T10:04:12.000Z",
"walletName": "easypaisa",
"accountNumber": "0312•••4567"
}
}401
Missing or invalid API key, or a key not attached to a merchant (or a signing error when signing is on).
Key problem
{
"success": false,
"error": "API key has no merchant association"
}Request signing is on and the signature failed
{
"success": false,
"error": {
"code": "SIGNATURE_INVALID",
"message": "Request signature rejected (bad_signature)."
}
}403
The calling IP is not on your allow-list.
{
"success": false,
"error": "IP_NOT_ALLOWED",
"message": "Your IP is not on this merchant's allowlist. Contact your account manager to add it."
}404
No payment with that reference for your merchant. This is the API's own answer. A 404 whose body is {"success":false,"error":"NOT_FOUND"} means the address was refused before it reached the API (a wrong path, or a reference or id with characters that cannot travel in a URL): it says nothing about whether the payment exists.
{
"success": false,
"error": "Transaction not found"
}429
Rate limit exceeded. Back off for Retry-After seconds.
500
Internal error — retry later.
{
"success": false,
"error": "Failed to retrieve transaction status"
}Payment result postback
Sent to your postback URL once, when the payment reaches its final state (completed or failed). You never receive one for pending.
Header X-Mint-Signature: sha256=<hex> is the HMAC-SHA256 of the raw request body with your postback secret. Verify it against the raw bytes you receive, before you parse them.
Any 2xx within 10 s acknowledges it. Otherwise we try again: 3 attempts in total, 5 s apart. A postback can arrive more than once, so record it once per transactionId + status.
amountis the amount paid;requestedAmountis your order amount.qris absent if no QR was ever created.qr.imageUrlisnull.mobile/customerMobileis the number the customer entered on the page.statusCodeEXPIRED(status: "failed"): no payment within the window, or the link expired before the customer submitted details.- A
failedpayment normally stays failed; nothing changes it by itself. If the payment network confirms that it was paid, AntPay staff correct it tocompletedand you then get a second postback (status: completed,statusCode: SUCCESS,pspStatusCode: MANUAL_SUCCESS). This is not automatic and can take hours or days. A latercompletedreplaces an earlierfailed.
Headers we send
| Header | Value |
|---|---|
Content-Type | application/json |
X-Mint-Signature | sha256= + hex HMAC-SHA256 of the raw body with your postback secret. See Verify the signature. |
Body
| Field | Type | Description |
|---|---|---|
success | boolean | API-call success only — NOT the payment outcome. Branch on |
reference | string | |
transactionId | string or null | Merchant-facing id, e.g. |
paymentMethod | string | |
mobile | string or null | |
amount | number | The amount in PKR: what was paid, and what settles. Reconcile on this. |
requestedAmount | number | Your order amount. |
status | "pending" "completed" "failed" | |
statusCode | "PENDING" "SUCCESS" "FAILED" "TIMEOUT" "INSUFFICIENT_FUNDS" "ACCOUNT_DEACTIVATED" … | |
pspStatusCode | string or null | Raw processor code. For analytics only: do not branch on it. |
pspOrderId | string or null | The id we sent to the payment processor. |
pspTxnId | string or null | The id the payment processor returned. |
message | string | |
failureReason | string or null | Non-null only when status is failed. |
attemptCount | integer | Present on the postback. |
attempts | array of objects | Present on the postback. |
qr | object |
|
createdAt | string (date-time) | |
updatedAt | string (date-time) | |
id | string | The |
vendorId | string | Identifies your API key. |
checkoutId | string | The |
acknowledged | boolean | Always |
tokenId | string | The |
customerMobile | string | The number the customer entered on the page (same value as |
user | object | The |
attempts[ ] object
| Field | Type | Description |
|---|---|---|
attemptNumber | integer | |
pspOrderId | string or null | |
pspTxnId | string or null | |
pspStoreId | string or null | |
pspStatusCode | string or null | |
message | string | |
outcome | "success" "failure" "timeout" "transport_error" "pending" | |
startedAt | string (date-time) | |
completedAt | string or null (date-time) | |
durationMs | integer or null |
qr object
| Field | Type | Description |
|---|---|---|
payload | string or null | Raw EMVCo QR text. Render it with any QR library exactly as received — never trim, re-encode, URL-encode or alter it; the string ends with a checksum and any change makes the code unscannable. |
imageUrl | string or null (uri) |
|
expiresAt | string or null (date-time) | ISO-8601, UTC. After this instant the QR cannot be paid. An unpaid payment is reported |
payableAmount | string or null | The exact amount the customer must pay, as a 2-decimal string ( |
Examples
Paid (completed / SUCCESS)
{
"success": true,
"reference": "ORD-10021",
"transactionId": "FDX-1A2B3C4D",
"paymentMethod": "qr",
"mobile": "03001234567",
"amount": 1500,
"status": "completed",
"statusCode": "SUCCESS",
"pspStatusCode": "SUCCESS",
"pspOrderId": "FDX-1A2B3C4D",
"pspTxnId": "01JABCDEF0000000000000000",
"message": "Payment completed successfully",
"failureReason": null,
"createdAt": "2026-11-01T10:00:00.000Z",
"updatedAt": "2026-11-01T10:04:12.000Z",
"attemptCount": 1,
"attempts": [
{
"attemptNumber": 1,
"pspOrderId": "FDX-1A2B3C4D",
"pspTxnId": "01JABCDEF0000000000000000",
"pspStoreId": null,
"pspStatusCode": "pending",
"message": "QR created",
"outcome": "success",
"startedAt": "2026-11-01T10:01:02.000Z",
"completedAt": "2026-11-01T10:01:03.000Z",
"durationMs": 850
}
],
"requestedAmount": 1500,
"qr": {
"payload": "000201010212…6304ABCD",
"imageUrl": null,
"expiresAt": "2026-11-01T10:16:02.000Z",
"payableAmount": "1500.00"
},
"id": "3f6c1d2e-8a4b-4c1e-9d7a-2b5e6f7a8c90",
"vendorId": "MERCHANT_000123_PK1",
"checkoutId": "3f6c1d2e-8a4b-4c1e-9d7a-2b5e6f7a8c90",
"acknowledged": false,
"tokenId": "3f6c1d2e-8a4b-4c1e-9d7a-2b5e6f7a8c90",
"customerMobile": "03001234567",
"user": {
"customerId": "C-778"
}
}Not paid in time (failed / EXPIRED)
{
"success": true,
"reference": "ORD-10021",
"transactionId": "FDX-1A2B3C4D",
"paymentMethod": "qr",
"mobile": "03001234567",
"amount": 1500,
"status": "failed",
"statusCode": "EXPIRED",
"pspStatusCode": "EXPIRED",
"pspOrderId": "FDX-1A2B3C4D",
"pspTxnId": "01JABCDEF0000000000000000",
"message": "QR code expired before it was paid",
"failureReason": "QR code expired before it was paid",
"createdAt": "2026-11-01T10:00:00.000Z",
"updatedAt": "2026-11-01T10:17:10.000Z",
"attemptCount": 1,
"attempts": [
{
"attemptNumber": 1,
"pspOrderId": "FDX-1A2B3C4D",
"pspTxnId": "01JABCDEF0000000000000000",
"pspStoreId": null,
"pspStatusCode": "pending",
"message": "QR created",
"outcome": "failure",
"startedAt": "2026-11-01T10:01:02.000Z",
"completedAt": "2026-11-01T10:01:03.000Z",
"durationMs": 850
}
],
"requestedAmount": 1500,
"qr": {
"payload": "000201010212…6304ABCD",
"imageUrl": null,
"expiresAt": "2026-11-01T10:16:02.000Z",
"payableAmount": "1500.00"
},
"id": "3f6c1d2e-8a4b-4c1e-9d7a-2b5e6f7a8c90",
"vendorId": "MERCHANT_000123_PK1",
"checkoutId": "3f6c1d2e-8a4b-4c1e-9d7a-2b5e6f7a8c90",
"acknowledged": false,
"tokenId": "3f6c1d2e-8a4b-4c1e-9d7a-2b5e6f7a8c90",
"customerMobile": "03001234567",
"user": {
"customerId": "C-778"
}
}
Your answer
Return any 2xx within 10 s to acknowledge. Otherwise we try again: 3 attempts in total, 5 s apart.