Connect once.
Keep every payment in view.
Create UPI collection requests, submit a UTR for review, and receive signed payment notifications. All amounts are INR. Call these APIs from your server; keep your secret out of browser and mobile code.
01 / Authenticate every request
Use HTTPS and a fresh cryptographically random nonce for each attempt. The timestamp is Unix seconds, accepted within ±300 seconds. The signature is lowercase hex HMAC-SHA256, using your API secret as UTF-8 bytes. Sign the exact body bytes sent on the wire.
X-PRN-Key: prn_... X-PRN-Timestamp: 1789560000 X-PRN-Nonce: random-string-at-least-16-characters X-PRN-Signature: lowercase-hex-hmac Canonical string (five lines, no final newline): HTTP_METHOD EXACT_PATH_AND_QUERY UNIX_TIMESTAMP NONCE LOWERCASE_HEX_SHA256_OF_BODY
The path includes the application virtual directory, if any, and the query string in its original order. For GET, hash an empty body. Nonces must be 16–80 letters, digits, underscores or hyphens. Reused nonces are rejected. Each merchant is limited to 120 requests per minute, with an additional IP limit.
02 / Create a payment
POST /Apis/v1.ashx/paymentsContent-Type: application/json
{
"order_id": "ORDER-1001",
"amount": 1250.00,
"customer": "Example Customer",
"customer_mobile": "9876543210"
}
order_id is 1–80 letters, digits, dots, underscores or hyphens. Customer names are 2–100 characters. customer_mobile is required and must contain 10–15 digits. Amounts must be positive, have at most two decimal places, and fit the merchant's configured limits. The server allocates an active UPI destination with sufficient capacity.
{
"success": true,
"reference_no": "PRN0123456789abcdef0123456789abcdef",
"merchant_order_id": "ORDER-1001",
"customer_name": "Example Customer",
"customer_mobile": "9876543210",
"amount": 1250.00,
"status": "PENDING",
"expires_at": "2026-09-16T12:00:00.000Z",
"upi_uri": "upi://pay?pa=...",
"qr_image": "data:image/png;base64,..."
}
Requests expire in 1 hour. The merchant/order pair is the idempotency key: retrying identical details returns the existing payment; changing the amount or customer returns 409. Use a fresh nonce for every retry. Creation returns 201, including an idempotent replay.
03 / Submit the UTR and proof
POST /Apis/v1.ashx/payments/utr{
"reference": "PRN0123456789abcdef0123456789abcdef",
"utr": "634512789012",
"proof_base64": "optional-raw-base64-without-data-prefix"
}
UTRs must contain 12–22 alphanumeric characters and are globally unique, case-insensitively. Images must be PNG or JPEG and smaller than 2 MB, with dimensions no greater than 4096 × 4096 and 12 million pixels. The decoded image is re-encoded and stored privately. The complete JSON request must be under 3,000,000 bytes. This API uses JSON base64 uploads so the entire proof is covered by the request signature.
Only the owning merchant can submit a UTR, while the payment is pending and unexpired. A screenshot and UTR require bank-record verification before approval.
04 / Check payment status
GET /Apis/v1.ashx/payments/status/Apis/v1.ashx/payments/status?reference=PRN0123456789abcdef0123456789abcdef /Apis/v1.ashx/payments/status?utr=634512789012
Supply exactly one reference or UTR. Results are restricted to the authenticated merchant. The response contains success and a data object with Reference, OrderId, Amount, Status, Utr, CreatedUtc, ReviewedUtc and other transaction fields.
05 / Verify callbacks
Approval and rejection queue durable notifications. Callback delivery has separate state from payment status. A worker retries unsuccessful deliveries up to eight times, with exponential delays starting at 60 seconds. Administrators can requeue failed events.
X-PRN-Event-ID: stable-event-uuid
X-PRN-Timestamp: unix-seconds-for-this-attempt
X-PRN-Signature: lowercase-hex-hmac
Webhook canonical string:
EVENT_ID
TIMESTAMP
RAW_JSON_BODY
{
"event": "payment.approved",
"reference_no": "PRN...",
"merchant_order_id": "ORDER-1001",
"amount": 1250.00,
"utr_no": "634512789012",
"status": "APPROVED",
"reviewed_at": "2026-09-16T12:01:00Z"
}
Verify HMAC with the API secret active when the event was queued, compare signatures in constant time, and reject timestamps outside your allowed window. Retain the previous secret for pending callbacks after rotation. Deduplicate by event ID and return a 2xx status after durable processing. Redirects are not followed. Callback endpoints require an operator-approved public HTTPS hostname on port 443.
Errors and states
{"success":false,"message":"Description","request_id":"correlation-id"}
| Status | Meaning |
|---|---|
| 400 | Invalid fields, JSON, limits, or HTTPS requirement |
| 401 / 403 | Invalid authentication / insufficient access or disallowed IP |
| 404 | Unknown endpoint or payment |
| 409 | Duplicate UTR/nonce/order, insufficient capacity, or conflicting state |
| 413 / 415 | Request too large / unsupported content type |
| 429 | Rate limit reached; retry later with a fresh nonce |
| 500 / 503 | Internal failure / unavailable database |
Payment states: PENDING → UTR_SUBMITTED → APPROVED or REJECTED. Unpaid requests become EXPIRED. Approval and rejection are final. Callback delivery is tracked independently as Pending, Retrying, Delivered, or Failed.
