Skip to content

Settlements

When, by which route and with what fee the money people pay reaches your account, and how to read and reconcile a settlement. About 10 minutes to read. You need a live API key with the settlements:read scope (a test key sees no settlements).

How the money moves

There are two routes; which one applies depends on whether the operator has switched settlements on for your app.

Settlements off (every app today) Settlements on
Capture the money goes straight to the app's payout account (for a sub-merchant's intent, to its approved payout account) the money goes to the app's clearing account (the app's own stream and each sub-merchant's are separate)
Fee none fixed per payment at capture, moved to the platform by the settlement
Refunds out of the payout account out of the clearing account
To the payout account on every capture by a settlement on the schedule: the net amount (payments − refunds − fees)
GET /settlements empty the settlements

Today no app in production has settlements on — every capture goes straight to the payout account, with no fee. Before switching settlements on for an app the operator sets up a payment rule (Fees) and a payout account (Payout accounts). A payment captured before the switch never enters a settlement (it keeps no fee, and its refunds come out of the payout account). If the app's settlements are switched off again, new captures go straight to the payout account again and whatever is left in clearing keeps being settled.

Schedule and cutoff

The schedule is part of the operator's payment rule (per app):

schedule When
daily every day at settle_time
weekdays Monday – Friday at settle_time (no public-holiday calendar yet)
weekly:mon … weekly:sun once a week, on that day
manual never by itself — the operator runs it by hand
  • settle_time is Ulaanbaatar time (HH:MM); delay_days (T+N) is 0–14.
  • Cutoff — how far a run reaches: with T+N, the payments captured up to the end of the day N days before the run's (Ulaanbaatar) day (for example T+1 at 10:00 → everything up to yesterday 24:00). T+0 covers everything up to the moment of the run.
  • With no rule, the default: no fee, weekdays at 10:00, T+1.
  • The settlement job runs every 15 minutes — a settlement may come up to 15 minutes after its scheduled time. A run that was missed is not lost: the next run's cutoff covers it.
  • A suspended app is not settled on schedule (only by the operator, by hand).

The Settlement object

The body of GET /settlements/{id} and GET /settlements, and data.object of the settlement.paid webhook:

{
  "id": "st_7QwErTy2UiOp4AsDf6GhJk8L",
  "object": "settlement",
  "status": "paid",
  "cutoff": "2026-10-21T16:00:00Z",
  "gross": 100000,
  "refunds": 5000,
  "reserve_refunds": 0,
  "fee": 2000,
  "net": 93000,
  "holdback": 0,
  "released": 0,
  "recovered": 0,
  "paid": 93000,
  "intent_count": 3,
  "destination_masked": "******0042",
  "holdback_release_at": null,
  "livemode": true,
  "created_at": "2026-10-22T08:58:00Z",
  "paid_at": "2026-10-22T08:59:00Z",
  "refund_count": 1,
  "fee_assessed": 2000,
  "fee_returned": 0,
  "fee_carried_in": 0,
  "fee_carried_out": 0,
  "clearing_carried_in": 0,
  "clearing_carried_out": 0,
  "failure_code": null,
  "sub_merchant": null
}
Field Meaning
cutoff the payments captured before this instant are in it
gross · intent_count the sum and number of the payments in it
refunds · refund_count the refunds deducted (of payments in this or an earlier settlement)
fee the fee this settlement took (fee_assessed − fee_returned + fee_carried_in − fee_carried_out); negative when it gave back more than it took
fee_assessed · fee_returned the payments' fees · the fees given back with refunds
fee_carried_in · fee_carried_out fee carried in from the previous settlement · carried on to the next (when there was nothing to take it from)
clearing_carried_in · clearing_carried_out refunds that left clearing after earlier settlements beyond what that settlement held — deducted here · carried on to the next
recovered clearing_carried_in − clearing_carried_out
net gross − refunds − fee
paid what was paid to the payout account, net − holdback + released − reserve_refunds − recovered (≥ 0; may be 0)
holdback · released · reserve_refunds · holdback_release_at always 0 / null — there is no holdback
destination_masked the payout account, last digits only
sub_merchant the sub-merchant's id for its stream, null for the app's own
failure_code when failed: insufficient_funds, destination_unavailable, limit_exceeded, idempotency_conflict, rejected
livemode always true

Fee shares and carries are whole ₮ and every identity is checked on the server — you need not recompute paid, but you may use these identities in your reconciliation.

Statuses

status Meaning
pending computed and frozen, being posted to the ledger (retried with the same key when the answer is unknown)
paid paid was moved to the payout account (paid_at); settlement.paid is sent
failed the ledger refused (failure_code) — the operator retries it with the same lines or voids it; a voided settlement's payments go into a later one

One stream of an app has at most one open (pending / failed) settlement — the next one waits for it. A voided (void) settlement is not shown to the app's backend; the console shows it. A settlement that moves nothing (all of it fee, or carried) is paid at once and sends settlement.paid with paid: 0.

Example

Rule: 2 % fee (fee_bps: 200), refund_fee: false, daily 10:00, T+1.

Date (UB) What happened In clearing To the payout account
10-21 3 payments: 10 000, 25 000, 65 000 (fees 200 + 500 + 1 300) 100 000 0
10-21 5 000 refunded from the 3rd payment 95 000 0
10-22 00:00 a 50 000 payment (fee 1 000) — exactly at the cutoff, so it goes into the next settlement 145 000 0
10-22 10:00 Settlement (cutoff 10-22 00:00): gross 100 000, refunds 5 000, fee 2 000 → paid 93 000 50 000 93 000
10-22 10 000 refunded from the 1st payment (already settled; clearing holds 50 000) 40 000 93 000
10-22 a 45 000 refund of the 3rd payment requested → clearing holds 40 000 < 45 000 → refund.failed (settlement_shortfall) 40 000 93 000
10-23 10:00 Settlement: gross 50 000, refunds 10 000, fee 1 000 → paid 39 000 0 132 000

Check: people paid 150 000 and got 15 000 back; the platform's fee is 3 000; you received 132 000 = 150 000 − 15 000 − 3 000.

Shortfall. A refund of a payment that was already settled to your account comes out of what clearing holds now (later payments). If clearing does not hold enough, the refund is refused with settlement_shortfall — receivables (advancing the refund and deducting it from later settlements) are not built yet; the operator sees a list of such refunds.

Carry. When refunds that left clearing after a settlement exceed the gross of the next one, the excess is deducted from the one after (clearing_carried_*); a fee with no money to take it from (a full refund with refund_fee: false, for example) moves to the next settlement (fee_carried_*).

Reading — the API

The app's backend (Basic with an API key holding settlements:read, or a person's token with the payments consent), live only — a test credential always gets an empty list.

curl -sS "https://geregeapp.mn/api/v1/miniapp/settlements?status=paid&limit=20" \
  -u "$GEREGE_CLIENT_ID:$GEREGE_CLIENT_SECRET"
# → {"items": [Settlement, …], "next_cursor": "st_…" | null, "has_more": true | false}
Parameter Meaning
status pending · paid · failed; anything else → 400 invalid_status
cursor next_cursor of the previous page; invalid → 400 invalid_cursor
limit 1 – 100, default 20; anything else → 400 invalid_limit

Newest first. GET /settlements/{id} is the Settlement + intents[] (id, amount, fee, reference, confirmed_at) + refund_items[] (id, intent_id, amount, fee_returned, source) + truncated — with more than 1000 lines, the first 1000 and truncated: true. Tell sub-merchants' settlements apart by sub_merchant. An installation without a settlement store answers 503 settlements_unavailable.

The developer console shows the same settlements per app (voided ones included). Every money row (JSON or CSV) is in Statements (also settlements:read).

Webhook

settlement.paid — when a settlement becomes paid, always livemode: true, data.object = Settlement. paid may be 0. Event reference.