Pay AGL API Documentation
Create payment links, collect UPI pay-ins, send bank payouts, check wallet balances, and receive signed webhooks from one merchant integration.
Quickstart for merchants
- Log in to the merchant dashboard.
- Open API & Webhooks and copy your live API key. Keep your API secret private; it is used to verify Pay AGL webhooks.
- Submit your server IP for approval in the IP whitelist section if your account uses whitelist enforcement.
- Configure pay-in and payout callback URLs.
- Use the examples below from your backend server, never from browser JavaScript.
https://payagl.com. All request and response bodies are JSON. Amounts are decimal numbers in your merchant currency, usually INR for Indian UPI/IMPS rails.Authentication
Send your live merchant API key on every API request.
x-api-key: pk_live_your_merchant_key Content-Type: application/json
| Status | Meaning |
|---|---|
401 | Invalid API key. |
403 | Merchant inactive, IP not whitelisted, or payout fee not configured. |
429 | Rate limited. |
POSTCreate payment
Creates a production pay-in through the active Pay AGL route and returns a hosted checkout URL plus PSP-issued UPI deeplinks. Pay AGL never returns a placeholder VPA. order_id is required and idempotent per merchant: retrying the exact same request returns the existing transaction; reusing it with different amount, currency, or destination returns 409. Callback and post-payment redirect URLs must use public https:// addresses. The request currency must match the merchant account currency.
curl -X POST https://payagl.com/api/payment/create \
-H "x-api-key: pk_live_your_merchant_key" \
-H "Content-Type: application/json" \
-d '{
"order_id": "ORD-10001",
"amount": 1500.00,
"currency": "INR",
"customer_name": "Ramesh Kumar",
"customer_email": "ramesh@example.com",
"customer_phone": "9999999999",
"description": "Order #10001",
"callback_url": "https://merchant.example/webhooks/payin",
"redirect_url_after": "https://merchant.example/thank-you"
}'
{
"txn_id": "TXN...",
"order_id": "ORD-10001",
"status": "PENDING",
"amount": 1500,
"currency": "INR",
"redirect_url": "/api/payment/redirect/TXN...",
"checkout_url": "/checkout.html?id=TXN...",
"payment_links": {
"ready": true,
"upi_deeplink": "upi://pay?...",
"phonepe_intent": "phonepe://pay?...",
"gpay_intent": "tez://upi/pay?...",
"paytm_intent": "paytmmp://pay?...",
"qr_image_url": null,
"expires_at": "2026-06-23T...",
"error": null
}
}
Hosted vs custom checkout
- Hosted checkout: redirect the customer to
checkout_url. - Custom checkout: open the correct value from
payment_linksfor PhonePe, GPay, Paytm, or generic UPI.
GETPayment links refresh
Use this only while a transaction is PENDING and payment_links.ready is false. A successful response contains a real, time-limited UPI URI issued by the active processor; never construct your own VPA.
curl "https://payagl.com/api/payment/TXN.../payment-links?retry=true" \ -H "x-api-key: pk_live_your_merchant_key"
{
"txn_id": "TXN...",
"order_id": "ORD-10001",
"status": "PENDING",
"payment_links": { "ready": true, "upi_deeplink": "upi://pay?..." }
}
GETPayment status
Check a payment using either the PayAGL txn_id or your submitted order_id. The lookup is restricted to the merchant authenticated by the API key.
curl https://payagl.com/api/payment/status/ORD-10001 -H "x-api-key: pk_live_your_merchant_key"
Path parameter: txn_id_or_order_id — pass the exact transaction ID or order ID (case-sensitive).
{
"txn_id": "TXN...",
"order_id": "ORD-10001",
"merchant_id": "PAGLMER000080",
"amount": 1500,
"currency": "INR",
"status": "SUCCESS",
"utr": "123456789012",
"completed_at": 1782220000000
}
Payment statuses: PENDING, SUCCESS, FAILED. utr is null until available.
GETCollection UPI / payment receipt
Look up your own pay-in by exact transaction ID or order ID. Uses the latest stored transaction status; this endpoint does not query the provider or verify that a UPI ID is active.
curl https://payagl.com/api/payment/upi/ORD-10001 -H "x-api-key: pk_live_your_merchant_key"
Successful payment (UPI ID is omitted):
{"txn_id":"TXN...","order_id":"ORD-10001","status":"SUCCESS","utr":"123456789012"}
Payment not received (UTR is omitted):
{"txn_id":"TXN...","order_id":"ORD-10001","status":"PENDING","upi_id":"collection@bank","message":"Payment not received"}
Failed, processing, and expired transactions retain their actual status. When no usable collection UPI deeplink was stored, upi_id is null with upi_message: "UPI ID unavailable". A successful payment whose UTR has not arrived returns utr: null. Unknown IDs and other merchants' IDs return HTTP 404. Standard API key, IP, and enabled request-signing policies apply.
POSTVerify payment or stored UPI ID
/api/payment/verify accepts a transaction ID or merchant order ID as reference. The order_id alias and UTR-only lookup are also supported. All lookups are restricted to the authenticated merchant's pay-ins. Existing GET status and UPI endpoints remain unchanged.
curl https://payagl.com/api/payment/verify -X POST -H "x-api-key: pk_live_your_merchant_key" -H "Content-Type: application/json" -d '{"reference":"ORD-10001"}'
Successful response (HTTP 200):
{"txn_id":"TXN...","order_id":"ORD-10001","status":"SUCCESS","upi_id":"collection@bank","utr":"123456789012","upi_assigned":true}
Pending and failed examples (HTTP 200):
{"txn_id":"TXN...","order_id":"ORD-10001","status":"PENDING","upi_id":"collection@bank","utr":null,"upi_assigned":true}
{"txn_id":"TXN...","order_id":"ORD-10002","status":"FAILED","upi_id":null,"utr":null,"upi_assigned":false}
UPI ID-only verification (no reference, order ID or UTR required):
curl https://payagl.com/api/payment/verify -X POST -H "x-api-key: pk_live_your_merchant_key" -H "Content-Type: application/json" -d '{"upi_id":"example@bank"}'
{"upi_id":"example@bank","status":"MATCH","verification_scope":"MERCHANT_STORED_RECORDS","live_bank_verified":false}
All three results return HTTP 200: MATCH means the normalized UPI ID matches a saved collection payee in your authorized records; NOT_FOUND means usable authorized evidence exists but contains no match; UNVERIFIED means no usable authorized evidence is available. These results do not establish payment receipt, current assignment, account activity, or live bank verification. NOT_FOUND does not mean the UPI ID is invalid at a bank.
Evidence is limited to your own pay-ins and saved payment links with a valid provider UPI payee URI, plus active pay-in pipelines explicitly assigned to you through current routing bands (positive allocations) or your pay-in pipeline setting. Pipeline records must contain a saved payee URI or an explicit upi_verified: true flag for their configured upi_id. Country compatibility is required. Global default pipelines, other merchants' records, payer VPAs, bare unverified configuration, and arbitrary provider response fields are excluded. No transaction, pipeline, credential, or other merchant details are returned. UPI ID-only lookup is case insensitive, validates syntax, and never changes stored records.
This endpoint uses the existing API key authentication, rate limit (120 authenticated requests per source IP per 10 seconds), IP restrictions, and enabled request signing. Enhanced merchants must sign the exact request body and path as usual. HTTP 400 indicates malformed input, 401/403 authentication or policy rejection, and 429 rate limiting. When any reference, order ID, or UTR is supplied, the existing transaction lookup response and matching rules below are unchanged.
Alternative request bodies:
{"order_id":"ORD-10001"}
{"utr":"123456789012"}
{"reference":"ORD-10001","upi_id":"collection@bank"}
Optional upi_id adds upi_matches: true or false to confirm whether that payee was assigned to this transaction. Assignment is derived only from the transaction's saved provider UPI URI. Missing UPI or UTR returns null. Status reflects the latest stored state, including processing or expired; this endpoint does not poll the provider or test UPI account activity. UPI assignment alone does not confirm payment.
Unknown references, other merchants' transactions, and conflicting supplied identifiers return HTTP 404 with {"detail":"Transaction not found"}. Missing or invalid identifiers return HTTP 400. When reference and UTR are supplied together, both must match. Standard API authentication, IP restrictions, and enabled request signing apply.
GETWallet balance
Returns pay-in wallet and payout wallet balances. API payouts debit the payout wallet.
curl https://payagl.com/api/wallet/balance \ -H "x-api-key: pk_live_your_merchant_key"
{
"merchant_id": "PAGLMER000080",
"currency": "INR",
"payin_wallet": {
"balance": 150000,
"pending_settlement": 5000,
"pending_topup": 0,
"pending_total": 5000,
"available": 145000
},
"payout_wallet": { "balance": 80000, "available": 80000 },
"balance": 150000,
"payout_wallet_balance": 80000,
"available": 145000
}
POSTCreate API payout
Sends a bank payout (IMPS or NEFT) from your payout wallet to a beneficiary account. Your merchant account must have API payout commission configured. total_debit = amount + payout_commission. order_id is idempotent per merchant. The current platform minimum is 150; merchant and route limits may be stricter and are enforced at request time.
curl -X POST https://payagl.com/api/payout/create \
-H "x-api-key: pk_live_your_merchant_key" \
-H "Content-Type: application/json" \
-d '{
"order_id": "WD-10001",
"amount": 500.00,
"method": "IMPS",
"beneficiary_name": "Ramesh Kumar",
"beneficiary_bank": "HDFC Bank",
"beneficiary_mobile": "9876543210",
"beneficiary_email": "ramesh@example.com",
"account_number": "123456789012",
"ifsc": "HDFC0001234",
"callback_url": "https://merchant.example/webhooks/payout"
}'
Submit payout requests to PayAGL as application/json. Include the actual bank name in beneficiary_bank and accurate beneficiary contact details. Use a 10-digit Indian mobile number; a country-code prefix is normalized to the last 10 digits for the current payout route. PayAGL handles conversion to the provider request format. callback_url is your PayAGL payout status webhook.
Required fields
| Field | Description |
|---|---|
order_id | Your required unique reference. Exact retries are idempotent; mismatched reuse returns 409. |
amount | Payout amount. Minimum 150. |
beneficiary_name | Account holder name. |
account_number | Beneficiary bank account number. |
ifsc | Beneficiary bank IFSC code. |
Optional fields
| Field | Description |
|---|---|
method | IMPS (default) or NEFT. |
callback_url | Optional per-request payout webhook override. Must be a public https:// URL. |
beneficiary_email, beneficiary_mobile | Beneficiary contact details. |
beneficiary_bank | Recommended: actual bank name, for example HDFC Bank. An IFSC code or branch value does not replace the bank name. |
beneficiary_branch | Optional branch name/address. This field is not used as the bank name. |
beneficiary_address, beneficiary_pincode | Beneficiary address and pincode. |
note | Payout note/narration. |
{
"payout_id": "PAYOUT...",
"order_id": "WD-10001",
"merchant_id": "PAGLMER000080",
"amount": 500,
"status": "PROCESSING",
"payout_commission": 10,
"total_debit": 510,
"provider_ref": "PAYOUT...",
"utr": ""
}
Payouts return PROCESSING first; the final SUCCESS/FAILED state is delivered to your callback_url webhook (and reflected in Payout status). On failure, the full total_debit is refunded to your payout wallet.
GETPayout status
curl https://payagl.com/api/payout/status/PAYOUT... \ -H "x-api-key: pk_live_your_merchant_key"
{
"payout_id": "PAYOUT...",
"order_id": "WD-10001",
"status": "SUCCESS",
"amount": 500,
"payout_commission": 10,
"total_debit": 510,
"provider_ref": "PAYOUT...",
"utr": "123456789012"
}
Payout statuses: PROCESSING, SUCCESS, FAILED.
Production routing and checkout
checkout_url. Pay AGL selects the active processor and supplies only a real PSP-issued UPI URI. Processor names, credentials, routing rules, and internal callback domains are never exposed to customers.- Do not generate, cache, or substitute your own UPI VPA or QR code.
- Treat
PENDINGas non-final. Credit an order only afterSUCCESSfrom a signed webhook or the status API. - Use a unique
order_idfor every order. Retrying the same ID is idempotent. - Store amount, IDs, status, UTR, and webhook delivery ID for reconciliation.
- Keep API keys server-side and whitelist only fixed production IP addresses.
Webhooks from Pay AGL
Pay AGL sends webhooks to your saved callback URL or the per-request callback_url. Verify the signature against the exact raw request body using your merchant API secret.
POST https://merchant.example/webhooks/payin Content-Type: application/json X-PayAGL-Signature: hex_hmac_sha256(api_secret, raw_body) X-PayAGL-Event: payment.pending | payment.success | payment.failed X-PayAGL-Delivery-Id: uuid
Payment webhook body
{
"txn_id": "TXN...",
"order_id": "ORD-10001",
"merchant_id": "PAGLMER000080",
"amount": 1500,
"currency": "INR",
"status": "SUCCESS",
"gateway_ref": "...",
"utr": "123456789012",
"completed_at": 1782220000000
}
Payout webhook body
{
"payout_id": "PAYOUT...",
"order_id": "WD-10001",
"merchant_id": "PAGLMER000080",
"amount": 500,
"status": "SUCCESS",
"utr": "123456789012",
"payout_commission": 10,
"total_debit": 510,
"completed_at": "2026-06-23T..."
}
Node.js verification
const crypto = require("crypto");
function verifyPayAGL(rawBody, signature, apiSecret) {
const expected = crypto
.createHmac("sha256", apiSecret)
.update(rawBody)
.digest("hex");
return typeof signature === "string" && /^[a-f0-9]{64}$/.test(signature) &&
crypto.timingSafeEqual(Buffer.from(expected, "hex"), Buffer.from(signature, "hex"));
}
POSTSupport tickets
Raise and track support tickets directly from your own dashboard or server using your API key. Tickets appear in the Pay AGL support console and our team replies there.
Create a ticket
curl -X POST https://payagl.com/api/support/tickets \
-H "x-api-key: pk_live_your_merchant_key" \
-H "Content-Type: application/json" \
-d '{
"subject": "Payout WD-10001 not credited",
"category": "Payouts",
"priority": "high",
"related_txn": "WD-10001",
"message": "Customer says the payout has not arrived after 2 hours."
}'
{
"ticket_id": "tk_ab12cd",
"subject": "Payout WD-10001 not credited",
"category": "Payouts",
"priority": "high",
"status": "open",
"related_txn": "WD-10001",
"created_at": "2026-06-25T...",
"updated_at": "2026-06-25T...",
"messages": [ { "by": "merchant", "at": "2026-06-25T...", "text": "Customer says..." } ]
}
Categories: General, Payments / Pay-in, Payouts, Settlements, API / Integration, KYC / Account, Dispute / Chargeback, Other. Priorities: low, normal, high, urgent.
GET List your tickets
curl "https://payagl.com/api/support/tickets?status=open" \ -H "x-api-key: pk_live_your_merchant_key"
GET Fetch one ticket
curl https://payagl.com/api/support/tickets/tk_ab12cd \ -H "x-api-key: pk_live_your_merchant_key"
Reply to a ticket
curl -X POST https://payagl.com/api/support/tickets/tk_ab12cd/reply \
-H "x-api-key: pk_live_your_merchant_key" \
-H "Content-Type: application/json" \
-d '{ "message": "Any update on this?" }'
Ticket statuses: open, pending (awaiting your reply), resolved, closed. Each message has by = merchant or support. Internal staff notes are never returned.
Integration assistance: @payagll on Telegram.
Error responses
Typical error body:
{ "detail": "message or validation details" }
| HTTP | Meaning |
|---|---|
400 | Bad request, insufficient balance, or invalid business state. |
401 | Invalid API key. |
403 | IP not whitelisted, merchant inactive, or payout fee not configured. |
404 | Transaction or payout not found for this merchant. |
409 | The same order_id was reused with different payment or payout details, or the request is already processing. |
422 | Validation error, missing fields, invalid amount or IFSC. |
429 | Rate limited. |
502 | Upstream gateway rejected the payout/payment. |
503 | No active payment route is available for the requested country, rail, or amount. |