# QR Code API

Accept payments in person. Mint static QR codes to print, or dynamic ones per transaction with the amount fixed.

The QR Code API mints **PayMV** codes — the Maldives National QR — for your outlets. A customer scans one in the Payer app and pays you; the money lands in your merchant wallet like any other payment.

There are two kinds, and the difference is who decides the amount:

| | Static | Dynamic |
| --- | --- | --- |
| Amount | the payer types it | you fix it |
| Lifetime | until you deactivate it | minutes |
| Image | we render and host a PNG | you render the payload |
| Made for | the standee on your counter | one per order, at a POS or checkout |

All endpoints live under `/qr-codes` and authenticate with a [secret key](/developers/docs/authentication) holding the `qr:write` scope. A publishable key cannot mint QR codes: a code commits you to accepting money at it, so it is a server-side operation.

## Create a code

```http title="POST /qr-codes"
POST /qr-codes
```

Send an `Idempotency-Key` so a retried create replays the first response instead of minting a second code.

### Body parameters

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `outlet_id` | integer | Yes | The outlet to attribute payments to. Must be one of yours. |
| `type` | string | No | `STATIC` (default) or `DYNAMIC`. |
| `amount` | integer | For `DYNAMIC` | Amount in **laari** (100 laari = 1 MVR). Rejected for a static code, where the payer enters it. |
| `expires_in_minutes` | integer | No | Dynamic only. Defaults to, and is capped at, the scheme maximum. |
| `title` | string | No | Shown to the payer. Defaults to the outlet name. Max 255 characters. |
| `description` | string | No | Extra line under the title. Max 255 characters. |
| `reference` | string | No | Your own identifier. You can fetch the code by it later. |

### A static code for the counter

```bash title="Request"
curl https://api.payer.app/qr-codes \
  -H "Authorization: Bearer sk_test_..." \
  -H "Idempotency-Key: counter_1" \
  -H "Content-Type: application/json" \
  -d '{
    "outlet_id": 42,
    "title": "Counter 1",
    "reference": "counter_1"
  }'
```

```json title="201 Created"
{
  "id": "5b3f2a1e-7c48-4d9a-9f60-2e8b1c4d7a91",
  "type": "STATIC",
  "status": "ACTIVE",
  "outletId": "42",
  "title": "Counter 1",
  "description": null,
  "amount": null,
  "payload": "00020101021135660014mv.favara.mpqr0108PAYRMVMV...6304F8EA",
  "qrid": "QR3EC6E56A",
  "imageUrl": "https://payer.sgp1.cdn.digitaloceanspaces.com/qr_codes/12/5b3f2a1e-....png",
  "reference": "counter_1",
  "expiresAt": null,
  "createdAt": "2026-07-31T09:14:22+00:00"
}
```

Print `imageUrl`, or render `payload` yourself if you want it in your own artwork. Create the code once and reuse it — you do not need a new one per customer.

### A dynamic code for an order

```bash title="Request"
curl https://api.payer.app/qr-codes \
  -H "Authorization: Bearer sk_test_..." \
  -H "Idempotency-Key: order_10482" \
  -H "Content-Type: application/json" \
  -d '{
    "outlet_id": 42,
    "type": "DYNAMIC",
    "amount": 25050,
    "expires_in_minutes": 5,
    "reference": "order_10482"
  }'
```

```json title="201 Created"
{
  "id": "0d0a4e6c-...",
  "type": "DYNAMIC",
  "status": "ACTIVE",
  "amount": 25050,
  "payload": "00020101021235660014mv.favara.mpqr...6304A1B2",
  "qrid": "QR8F21C0DE",
  "imageUrl": null,
  "reference": "order_10482",
  "expiresAt": "2026-07-31T09:19:22+00:00"
}
```

The payer is charged **exactly** `amount` — a payment for any other value is rejected — and the code stops working at `expiresAt`, including between the customer opening the app and confirming.

> [!NOTE]
> A dynamic code has no `imageUrl`. It lives for minutes, so hosting an image for each one would be wasted work on both sides. Encode the `payload` string with any QR library — that string is the whole QR content, nothing else goes into it.

Rendering the payload, for example in JavaScript:

```js title="Render the payload"
import QRCode from "qrcode";

const { payload } = await createQrCode({ amount: 25050 });
await QRCode.toCanvas(document.getElementById("qr"), payload, { width: 320 });
```

### Errors

| Status | Meaning |
| --- | --- |
| `401` | Missing or invalid key. |
| `403` | The key does not hold `qr:write`. |
| `404` | `outlet_id` is unknown, or belongs to another merchant. |
| `422` | A dynamic code without an amount, a static code with one, or an expiry outside the allowed range. |
| `503` | The image could not be rendered or stored (static only). Retry. |

## Fetch a code

```http title="GET /qr-codes/{id}"
GET /qr-codes/{id}
```

`{id}` is the `id` we returned **or** your own `reference`, so you can look a code up by the order number you already have.

```bash title="Request"
curl https://api.payer.app/qr-codes/order_10482 \
  -H "Authorization: Bearer sk_test_..."
```

Codes belonging to another merchant return `404`, the same as ones that do not exist.

## List codes

```http title="GET /qr-codes"
GET /qr-codes?outlet_id=42&type=DYNAMIC&limit=25&offset=0
```

Newest first. `limit` defaults to 25 and is capped at 100. All four parameters are optional.

## Deactivate a code

```http title="POST /qr-codes/{id}/deactivate"
POST /qr-codes/{id}/deactivate
```

Stops a code being payable — use it when a standee is retired or an order is cancelled. Send an `Idempotency-Key` so a retried call is safe.

```json title="200 OK"
{
  "id": "5b3f2a1e-...",
  "status": "INACTIVE",
  "type": "STATIC"
}
```

The code is kept rather than deleted: a printed code is part of your payment history, and a customer who scans it afterwards is told it is no longer active. Deactivating twice returns `400`.

## Reconciling payments

Every code carries a **QRID** (`qrid`) — the identifier the Maldives National QR standard uses to tie a code to the payment it produced. It is recorded against the resulting transaction, so it is the field to reconcile on. Pair it with your own `reference` to trace a payment back to the order that created the code.

## Displaying a code

The standard sets out how a PayMV code must be presented — issuer logo, merchant name, the QRID, and the "Maldives National QR" band, in the scheme's blue. We return the payload and, for static codes, a plain PNG; the surrounding card is yours to produce for now. Follow the PayMV branding rules when you design it.
