Skip to content

Limits

Use this page to check, in one place, every numeric limit: amounts, lengths, times, counts, page sizes and rate limits. Every number is the value in the module's code; what you get when you exceed one is in Error codes.

Amounts and text

What Limit When exceeded
An intent's amount whole ₮, 100 – 50 000 000 400 validation_error (amount)
The app's max_amount (set by the operator; the ceiling for one payment) 100 – 50 000 000, or none 400 validation_error (amount)
currency MNT only 400 validation_error
description 1 – 80 characters (shown to the person) 400 validation_error
reference ≤ 64 characters, no line breaks or tabs 400 validation_error
metadata ≤ 20 keys; key 1 – 40, value ≤ 500 characters 400 validation_error
min_age 13 – 21, not below the app's 400 validation_error
return_url https, host in the app's return_hosts, ≤ 2048 400 validation_error
A refund's amount ≥ 1, refunds in total not above what was paid; omitted = everything left 400 validation_error
A refund's reason ≤ 255 characters 400 validation_error
Withdrawing from a wallet to a bank 1 000 – 50 000 000 ₮ (Withdrawals to a bank) 400 invalid_amount
Idempotency-Key 1 – 128 printable ASCII characters, no spaces (1 – 100 for the console dashboard's actions, 8 – 128 for wallet withdrawals) 400 idempotency_key_required

Times

What Value
expires_in (time to pay) 60 – 1 800 seconds, default 600
capture_within (manual hold) 60 – 604 800 seconds (7 days), default 7 days; only with capture_method: "manual"
The hold's safety margin (on the ledger's side) automatic: expires_at + 15 minutes; manual: capture_before + half the window (1 – 15 minutes) — the platform releases at capture_before itself; this is only a backstop
An unconfirmed claim lapses after 90 seconds; the next person may take it
Unconfirmed claims one person may hold at once ≤ 3
Refund window 90 days after the payment
Launch code 60 seconds, single use
Access token 30 minutes
Refresh chain (grant) 8 hours from the launch
Payout link request expired when unanswered for 7 days
An API key's expires_at optional; when given, 1 hour to 3 years ahead
Overlap when rolling a key 0 – 604 800 seconds (7 days), default 24 hours
Statement and statistics range (from/to) Ulaanbaatar dates, the last 30 days by default, ≤ 366 days
Launches kept (analytics' launches, unique_persons) 400 days; earlier days count 0 (consent and payment counts are unaffected)

Webhooks

What Value
Time to answer a 2xx within 10 seconds (connect 5 seconds)
Retry schedule 1m, 5m, 30m, 2h, 6h, 12h, 24h — 8 attempts in all (~45 hours from first to last), then failed
Window for the signature's t ±300 seconds
Overlap when rotating the secret the previous secret keeps signing for 24 hours
Events kept (the console's delivery list, GET /events) 30 days after they were made (not while a delivery is still pending)
Webhook endpoints ≤ 5 per app and environment (endpoint №1 included); description ≤ 200; event_types ≤ 20 (empty = all)
An endpoint turned off by itself every delivery failing for 24 hours — the admins are warned; for 5 days — auto_disabled
webhook.test (console and POST /webhook-endpoints/{id}/test) 20/minute

More: Delivery and retries.

Registry and counts

What Limit
API keys ≤ 20 active per app and environment; name ≤ 60; IP allowlist ≤ 20 CIDRs; revocation reason ≤ 200
Test users ≤ 50 per app
Terminal batch 1 – 500 rows per request (JSON or CSV, body ≤ 1 MiB)
Sticker PDF (print-sheet) ≤ 500 terminals per sheet; without terminal_ids every active one (choose when there are more than 500); layouts a4-24 · label-50 · label-40 · tent-a6; QR ≥ 25 mm
Statement (/statements) ≤ 100 000 rows (422 statement_too_large)
return_hosts ≤ 5, exact hosts (shop.mn) — no scheme, port, path or wildcard
A terminal's label · location 1 – 60 · ≤ 120 characters
A terminal's reference ^[A-Za-z0-9_.:-]{1,64}$, unique within the app
A terminal's open intents one unclaimed per terminal per environment (409 terminal_busy)
A sub-merchant's code ^[A-Za-z0-9_.:-]{1,40}$
A sub-merchant's display_name · legal_name · reg_no 1 – 80 · ≤ 160 · ≤ 20 characters
Admins asked to approve a payout link up to the organisation's first 100 admins are notified
Pending wallet withdrawals ≤ 3 per owner

Pagination

List limit Default Shape
GET /payment-intents 1 – 100 20 {data, has_more}, after = the last id of the previous page
GET /settlements 1 – 100 20 {items, next_cursor, has_more}
GET /settlements/{id} lines — 1 000 truncated: true when there are more
GET /events 1 – 100 50 {data, next_cursor}, oldest first
GET /terminals, GET /sub-merchants, GET /webhook-endpoints — all {data}, unpaginated
GET /sub-merchants/{sid}/payout-links — the last 50 {data}
Console: payments, refunds (/dev/payments, /dev/refunds) 1 – 100 25 {data, next_cursor}
Console: API keys 1 – 100 50 {data, next_cursor}
Console: webhook deliveries 1 – 100 50 {items, next_cursor}
Console: test intents — 20 {items, next_cursor}

More: API conventions.

Request sizes

Request Largest body
POST /payment-intents, POST /oauth/token 16 KiB
Refunds, terminals, sub-merchants, payout links, a person's confirmation 4 KiB
A terminal batch, a sticker-PDF request 1 MiB
The console's API keys 8 KiB
Editing an app in the console 32 KiB

Beyond it: 400 invalid_request.

Rate limits

The platform's rate limiter counts each limit per caller (per minute; burst is the extra allowed in a short spike). Exceeding it answers 429 with {"error": "…"} (no code, http_429 in the SDKs) — back off and retry with the same Idempotency-Key.

Route Per minute Burst
POST /oauth/token 120 30
GET /me 600 120
/payment-intents…, /settlements…, /terminals…, /sub-merchants…, GET /events… 600 120
POST /terminals/print-sheet 30 10
POST /sub-merchants/{sid}/payout-link (each sends somebody a notification) 30 10
/webhook-endpoints… 120 30
POST /webhook-endpoints/{id}/test 20 5
GET /statements 60 10
Console: creating, rolling an API key 30 10
Console: dashboard capture · cancel · unclaim / refunds, statements 60 / 30 20 / 10
Public GET /pay/{token}, GET /pay/t/{code} 120 30
Person: POST /payments/claim, …/confirm, opening an app 30 10
Person: a wallet-to-bank withdrawal request 6 3

Coming soon

Built in 1.13.0: expires_in up to 604 800 seconds (7 days, an invoice) for qr and redirect intents — app and terminal keep 1 800; a key's IP allowlist (≤ 20).