One call creates a QR any Pakistani bank or wallet app can pay. We mint it, host the payment page, detect the payment and tell you. You send an amount and your own reference.
Get a key from the merchant portal, then:
curl -X POST https://api.qrix.uno/api/v1/qr \
-H "Authorization: Bearer qrix_live_..." \
-H "Content-Type: application/json" \
-H "Idempotency-Key: order-1001" \
-d '{
"amount": "1500.00",
"reference": "ORDER-1001",
"email": "customer@example.com"
}'
You get back a QR payload, an image URL and a hosted payment page:
{
"id": "01K…",
"reference": "ORDER-1001",
"amount": "1500.00",
"status": "pending",
"qrCode": "000201010211…6304A1B2",
"qrImageUrl": "https://pay.qrix.uno/4z91qk6t/qr.png",
"payUrl": "https://pay.qrix.uno/4z91qk6t",
"expiresAt": "2026-09-12T09:00:00.000Z"
}
Render qrCode yourself, use qrImageUrl, or just send the customer
payUrl. All three are the same payment.
Every request carries your secret key as a bearer token.
Authorization: Bearer qrix_live_a1b2c3d4_…
| Field | Required | Notes |
|---|---|---|
amount | yes | Rupees as a string, up to 2 decimals — "1500.00" |
reference | yes | Your own identifier. Echoed back everywhere, including webhooks |
description | no | Shown to the payer on the payment page |
email | no | Records who the QR is for. We do not contact them — see below |
mobile | no | Pakistani mobile, 03… or +923… |
expiresInDays | no | Defaults to 15. 1–365 |
submerchant | no | organisation, userId, name, city, country |
metadata | no | Your own key/value pairs, returned unchanged |
409 rather than the wrong answer.
email and mobile are stored
against the payment for your own reporting and reconciliation, nothing more. Send them
payUrl, qrImageUrl or the raw qrCode however you already
reach your customers — your templates, your sending domain, your reputation. We tell
you the moment it is paid, by webhook.
If you have branches, tenants or agents, populate submerchant now — we store and
return it unchanged today, so your integration is ready when it starts driving behaviour.
"submerchant": {
"organisation": "Branch 12",
"userId": "agent-88",
"name": "Gulberg Outlet",
"city": "Lahore",
"country": "PK"
}
Both answer from our records — fast, and reflecting everything we have detected. Poll these.
Forces a live re-check with the bank, right now. Use it when you believe a payment happened and our record disagrees. Rate-limited, because it is a synchronous call to a third party.
| status | Meaning |
|---|---|
pending | Not paid yet |
paid | Money received. paidAt is set |
expired | Past its expiry and never paid |
failed | The payment attempt failed |
Set your URL in the portal and we POST when a payment is paid. We retry with backoff for about fifteen hours if your server is unreachable.
POST https://yourserver.com/qrix/webhook
QRIX-Signature: t=1787304178,v1=9f8e7d…
QRIX-Event-Id: evt_01K…_paid
QRIX-Event-Type: payment.paid
{
"type": "payment.paid",
"id": "evt_01K…_paid",
"createdAt": "2026-08-28T06:37:12.081Z",
"data": {
"id": "01K…",
"reference": "ORDER-1001",
"amount": "1500.00",
"status": "paid",
"paidAt": "2026-08-28T06:37:12.081Z"
}
}
The header is t=<unix>,v1=<hmac>, where the HMAC is SHA-256 over
"<t>.<raw body>" using your signing secret. The timestamp is inside the
signed material, so a captured delivery cannot be replayed later.
const [t, v1] = header.split(',').map(p => p.split('=')[1])
const expected = crypto.createHmac('sha256', secret)
.update(t + '.' + rawBody).digest('hex')
const ok = crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(v1))
&& Math.abs(Date.now()/1000 - Number(t)) < 300
2xx quickly,
and do your work afterwards. Deduplicate on QRIX-Event-Id — it is stable across retries.
A complete copy at https://sbx-api.qrix.uno with its own data and its own keys.
Nothing there is real and nothing there can move money.
POST /api/v1/qr/:id/simulate with {"outcome":"paid"} or {"outcome":"failed"} when you need it to happen now, or to happen badly.{ "error": { "code": "validation_failed", "message": "Please check the highlighted fields",
"fields": { "amount": ["Amount must be a number with up to 2 decimals"] },
"requestId": "req_a1b2c3" } }
| Status | Meaning |
|---|---|
400 | Something in your request is invalid — fields says what |
401 | Missing, malformed or revoked API key |
404 | No such QR on your account |
409 | Idempotency key reused with different parameters |
429 | Too many requests — back off and retry |
Quote requestId when you contact us; it identifies the exact request in our logs.