Skip to content

Changelog

Every version of the «Mini apps» module (mn.gerege.miniapp), newest first. The API's version is the module's (info.version of sdk/openapi/gerege.v1.yaml). ⚠ marks a change an existing integration must adapt to.

1.15.1 — 2026-11-02

Fix. Notices an organisation's administrators must act on are mailed as well; a person's payment notices stay bell and push only; the console is at /apps/miniapp.

Since core 1.83.1 a notification rings the bell only unless it asks to be mailed (nexus.Notification.SendEmail). Mailed as well: miniapp.kyb.* (the operator's decisions, the queue's news), miniapp.agreement.signed, miniapp.key.live_created|live_rolled|live_revoked|expiring, miniapp.webhook.failing|disabled, miniapp.payout.self_set, miniapp.payout_link.requested — rare, work to do, and an administrator need not have the app on a phone. Not mailed: miniapp.payment.captured|refunded — they come with every purchase, the push has reached the phone that paid, and the history is in the wallet. The decision is notify.go's mailed table, and a test keeps every new kind in it. Menus and notification links are /apps/miniapp/… (core 1.81.0 moved /module/… to /apps/…), the wallet /account/wallet (core 1.80.0).

1.15.0 — 2026-11-02

Feature. An organisation's admin points an app's payout at its own Gerege Wallet account; review needs an active live webhook endpoint; launch rows are kept for 400 days.

PUT /api/v1/miniapp/dev/apps/{id}/payout {account_id}: the organisation's admin, after a fresh sign-in, outside a sandbox — an active MNT account whose holder is org:<this organisation>; any other is 422 account_not_owned (ask the operator), an unusable one 422 account_unavailable. Audited payout_account_set, the operator's admins told miniapp.payout.self_set. Earlier payments and their refunds keep their account; the operator's PUT /review/apps/{id}/payout is unchanged. Submission and approval need an active live endpoint (№1 or one added by the API) rather than webhook_url — disabled/auto_disabled do not count; the issue (not_ready, webhook_url) keeps its shape. miniapp_launches older than 400 days are deleted by the hourly purge, app by app, 5000 at a time (analytics' 366 days stay whole). Payment holds obey KYC limits (bank 1.5.0) — over one is 403 limit_exceeded and the intent fails.

1.14.0 — 2026-10-29

Feature. The business profile (KYB): an organisation fills in its form, proves its contacts with a code, attaches its documents and submits; the operator approves it with a tier and limits. A go-live checklist computed by the server.

The legal name and register come from what the core recorded when an eID-listed representative opened the organisation (tenant_profiles) — core 1.68.0 removed ХУР (XYP), so the tax number, address, director and owners are typed by the organisation and checked by the operator against the registration certificate. Support, technical and finance e-mail are proved through the core's nexus.EmailVerifier (core 1.70.0), the support phone by an SMS code (phone 1.1.0); the website, logo (512×512 PNG) and legal pages are probed at submission through the SSRF-guarded client. Documents go to nexus.Files. Only the organisation's admin writes and submits (submission after a fresh sign-in). Operator: the /review/merchants queue, approve (tier new | standard | custom), request-info, reject, restrict/unrestrict, limits — all audited, the admins told. GET /dev/account and /dev/apps/{id}/go-live give every step from the server. With MINIAPP_GO_LIVE=on an app's submission needs the agreement and a submitted profile, and a live intent the agreement, an approved unrestricted profile and the tier's limits (per payment, and per Ulaanbaatar day under the organisation's lock); the default, off, computes and shows and refuses nothing.

1.13.0 — 2026-10-27

Feature. Live keys and IP allowlists from the console behind a fresh sign-in, a person's sandbox organisation, permissions on live webhook endpoints, a terminal on QR payments, seven-day invoice-style intents and a phishing guard on payment descriptions.

Creating, rolling or re-addressing a live key needs miniapp.keys.live and a sign-in within the last ten minutes (the core's nexus.FreshSignIn); so does a live secret from the old POST /credentials, whose test secret needs miniapp.develop. IP allowlists work, on the core's nexus.ClientIP (behind its trusted proxies). Submitting for review counts a named live key; a revoked default key no longer counts. A person without a legal entity gets a sandbox organisation with this app installed: test mode works in full, while live keys, review, agreements and payout links answer 409 sandbox_only, and its app slugs begin with sbx- (which no other organisation may use). Webhook endpoint writes need miniapp.develop, and a live endpoint's URL, secret or deletion (endpoint №1 and POST /webhook too) miniapp.keys.live and a fresh sign-in; GET /dev/webhook-event-types lists what an endpoint may subscribe to, labelled mn/en. Console: the member's name on a payment's timeline (actor_name), and every terminal and sub-merchant of the organisation (GET /dev/terminals, /dev/sub-merchants). Payments: a qr intent may name its terminal (a kiosk, a cash register — the one-open-basket rule stays the terminal presentment's), qr/redirect expires_in up to seven days, a web or e-mail address or a phone number in the description is 400 description_not_allowed, and GET /payment-intents/{id} carries its refunds. Fixes: payments already taken for a sub-merchant that is then disabled are still captured; confirming an unclaimed test intent from the console is 409 not_claimed, not a 500; console test intents keep capture_within and min_age; a tester's launch code is redeemed only with the test credential; app.status_changed, consent.revoked and settlement.paid get the inline first attempt; /events?type=webhook.test is 400 test_event_not_listed; insufficient_scope carries code.

1.12.0 — 2026-10-27

Feature. Merchant agreements: the operator publishes versioned texts, and an organisation's representative signs one in its name with eID.

Agreements are of kind merchant, developer or dpa, date-versioned (2026-11-01), with the Mongolian original and an English translation; a published text never changes. «Sign with eID» renders a PDF on the server with the organisation's name and registration number, a document number and the text's SHA-256, and puts it to the person's phone through the core's OrganisationSigner (PAdES-BASELINE-LT, eID onBehalfOf) — eID itself checks the representation. The signed PDF, its hash and the unsigned document's, the version, the organisation as eID confirmed it, the signer's name, certificate and the time are kept; the person's registration number is not stored in the clear. Starting one needs 1.8.0's miniapp.agreements.sign (admins only) and no fresh sign-in — PIN2 is the proof. The console polls the ceremony; job agreement-signatures finishes it if the console is closed.

1.11.0 — 2026-10-27

Feature. Terminal tools: batch registration (JSON or CSV), a sticker PDF (QR, code, NFC mark, crop marks) and per-terminal stats.

POST /terminals/batch (the console and live credentials; a test credential gets 403 live_only) registers up to 500 terminals in one request: every row is checked before anything is written and one bad row refuses the batch; a row with a registered reference answers that terminal (existing), and sub_merchant_code names the owner; a CSV's header row is label, location, reference, sub_merchant_code. POST/GET /terminals/print-sheet is a PDF of the chosen (or every active) terminal: 24 labels on A4, single 50 and 40 mm labels (40 mm is the NFC sticker) and a desk tent card; each label has a QR of the terminal's url (error correction Q, ≥ 25 mm), «Pay with Gerege App», the label, the location, the code to type, an NFC mark, crop marks and a TrimBox; Mongolian is set in embedded Montserrat. GET /dev/apps/{id}/terminals/{tid}/stats gives a day-by-day count of intents, claims and statuses, the captured and refunded amounts, the latest payment and the open basket — never who paid.

1.10.0 — 2026-10-27

Feature. The developer dashboard: every payment of the organisation listed, each one's detail and timeline, capture / cancel / refund / release a claim from the console, statements, sandbox simulation, and the organisation's audit log. Privacy fix: an app's history no longer shows the organisation the ids of the operator's staff or of payout account holders.

GET /dev/payments filters by app, environment, status, presentment, terminal, sub-merchant, dates, amounts and a search (pi_…, reference, user_ref), paged by cursor, or exports format=csv (≤ 100 000 rows, miniapp.finance). GET /dev/payments/{pi} adds the refunds, a timeline (who: app, person, job, dashboard:<member>) and the webhooks. POST …/capture, …/cancel, …/unclaim and …/refunds (a reason required) take exactly the app backend's path (the same guards, ledger keys and events), need an Idempotency-Key and miniapp.payments, and are audited; a live refund of 1 000 000₮ or more needs a sign-in within the last ten minutes. GET /dev/refunds. Statements: GET /dev/statements and the merchant backend's GET /statements — every money movement by Ulaanbaatar day (captured, refunded, fee, settlement_paid, carries) with the clearing balance, as JSON or CSV (UTF-8 BOM). Sandbox: claimless (qr, redirect, terminal) test intents from the console with their pay_url, …/simulate (claim, confirm, decline, expire) and a terminal's …/simulate-tap — the same webhooks as the real flow. Analytics break payments down by presentment, terminal and sub-merchant. GET /dev/audit is the organisation's whole history, every app's and its own; nobody outside the organisation is named by id. No dashboard response carries a person's user_id. Privacy fix: GET /dev/apps/{id}/audit used to show every member who could read the app the actor_user_id of the operator's staff and of payout account holders (and an account owner's id in the operator's payout row); like /dev/audit it now names only the organisation's own members by id and name, and everybody else by kind (actor.kind: operator | person) with actor_user_id null.

1.9.0 — 2026-10-27

Feature. Several webhook endpoints (up to five per environment, each with an event filter, its own secret and delivery log) and an events API to fetch what was missed.

The app's existing webhook_url became endpoint №1 in live and in test — the same URL, secret and signatures, no pending event lost; POST /dev/apps/{id}/webhook stays. Endpoints are managed from the console (/dev/apps/{id}/webhook-endpoints) and with client credentials (/webhook-endpoints): URL, description, event_types (empty = all), active/disabled, secret rotation (the old one keeps signing for 24 hours), a test send per endpoint. The delivery log filters by endpoint (endpoint_id, type) and shows each event's attempts per endpoint; a redelivery may name the endpoint. An endpoint failing every delivery for a day gets the organisation's admins warned, for five days it is turned off (auto_disabled). GET /events, /events/{id}: the same objects as the webhooks, in the credential's environment, thirty days, a (created_at, id) cursor.

1.8.0 — 2026-10-27

Feature. API keys and scopes: many named keys per app, scoped rights, rolling with an overlap, revocation, last-used time; the developer platform's new permissions.

The console creates test keys (POST /dev/apps/{id}/keys): a name, scopes (payments:read, payments:write, refunds:write, terminals:, sub_merchants:, payout_links:write, settlements:read, events:read, webhooks:write, oauth:token) or a preset, an expiry. The client id is gk_test_…, the secret gks_test_…<checksum>, shown once. A roll issues a new key and stops the old one after an overlap of 0 – 7 days; a revocation is immediate. Every server route names the scope it needs and refuses a key without it with 403 insufficient_scope (required_scope). Test keys never get the scopes that write terminals, sub-merchants, payout links or webhook endpoints. The existing mapp… pair is the «default key» with every scope of its environment and keeps working; a new secret from POST /credentials stops the old one at once. Live keys through the new API and IP allowlists stay closed until the core offers a fresh sign-in and the caller's address (Wave 2) — live secrets still come from POST /credentials. New permissions: miniapp.develop (managers), miniapp.keys.live (administrators only — a manager does not reach the new live-key API), miniapp.payments, miniapp.finance (managers), miniapp.agreements.sign (administrators). The «Developers» menu group: apps and team (the core's members screen); overview, payments, balance, business and KYB entries arrive with their console screens in the core's next release. Notices seven days and one day before a key expires, and to the administrators on every change to a live key.

1.7.0 — 2026-10-27

Feature. «Pay with Gerege App»: merchant apps, QR / redirect / terminal payments that become a person's when claimed, sub-merchants and their settlements.

A kind=merchant app is never in the launcher and needs no entry URL or manifest (review is the legal pages and the operator). A merchant's backend creates intents with client credentials — presentment qr, redirect (back to a registered return host) or terminal — and gets a pay_url; a person scans the QR or taps the terminal's sticker or NFC tag, POST /payments/claim admits them (age, eID) and the ordinary payment sheet pays it. The merchant learns a random user_ref per payment. One open intent per terminal; capture_within (60 s – 7 days) for manual capture; payment_intent.claimed and payment_intent.expired events; every enqueued webhook is attempted at once. Each sub-merchant has its own clearing account 2500-<seg> and settlements, paid to an account its holder — the person, or an admin of the owning organisation — approved in Gerege App. The operator's payout records the account's holder. Public GET /pay/{token} and /pay/t/{code} for the shell's desktop page.

1.6.0 — 2026-10-26

Feature. Banner sections and «coming soon» services in the launcher's home layout.

A new «banner» section: 1–10 pictures (an https:// address or a /brand/… path), a title, a subtitle, and on tap a mini app (checked at publication) or an https link. featured, grid and category sections take «soon» tiles — services that are not mini apps yet, a name and an icon; the app answers a tap with «coming soon», and the operator swaps in the real app's slug once it is published. A banner for an app the person cannot open is dropped; a section of soon tiles alone stays.

1.5.0 — 2026-10-23

Feature. The person who paid is notified when a payment is taken and when it is refunded (bell and push).

Once the ledger confirms a capture the person gets «Payment made» (amount, app name), and once a refund succeeds «Payment refunded» (amount, app name; the app's own text never reaches the push); tapping it opens the wallet. Kinds are miniapp.payment.captured / miniapp.payment.refunded, keyed by the intent or refund id, so one event is told once — also for manual captures and calls the reconcile job finishes. Test-mode payments notify nobody; a notification that fails never fails the payment.

1.4.0 — 2026-10-23

Feature. The profile.phone scope: an SMS-verified phone number, with the person's consent.

An app whose manifest declares profile.phone asks the person in the host's consent screen, like e-mail; /me and the launch profile then carry the number in E.164 (+97699112233) from mn.gerege.phone. A person with no verified number sends no field at all.

1.3.0 — 2026-10-22

Feature. Settlements, phase 1: operator-set dynamic fees and schedules, a clearing account per app, T+N net payouts and the settlement.paid webhook. Behind a switch (MINIAPP_SETTLEMENTS=off|shadow|on).

Operators set versioned payment rules per app (effective_from; a rule in effect is never edited): fee = round_half_up(amount × fee_bps / 10000) + fee_fixed, within fee_min/fee_max, refund_fee; schedule daily / weekdays / weekly:<day> / manual, Ulaanbaatar time, T+N. No rule means no fee, weekdays at 10:00, T+1. For an app switched on, captures go to its 2500-<seg> clearing account and settlements move them to 4500 (fee) and the payout account (net); no holdback. Refunds of settled payments come out of clearing; when it cannot cover one the refund is refused as settlement_shortfall (receivables are phase 2). Payments captured before the switch keep the old path. API: /review/apps/{id}/payment-rules, /settlement, /settle, /review/settlements, /review/refund-shortfalls; /dev/apps/{id}/payment-rule, /settlements; GET /api/v1/miniapp/settlements. Intents carry fee/net.

1.2.0 — 2026-10-22

Feature. OMAS 1.0 conformance, a developer sandbox (webhook deliveries, test users, test payments), operator-managed launcher layouts and developer analytics.

Launch enforces requires_verified/min_age (403 verification_required / age_restricted); token and /me carry OMAS ref; the manifest check probes the 512 icon (PNG 512×512) and the legal pages; operator max_amount. Console API: webhook/deliveries (+retry, test event), test-users (active members of the organisation only), test-intents (+confirm, OMAS test amounts). app.status_changed and consent.revoked events. Operators publish and roll back versioned launcher layouts; persons read GET /api/v1/miniapp/layout. Developers get daily launches, persons, consents and payments (Ulaanbaatar days).

1.1.0 — 2026-10-22

Feature. Mini app payments: payment intents, the person's confirmation as a core-server hold, capture to the app's payout account, release on cancel or expiry, refunds, and signed webhooks.

A mini app's backend creates an intent with the person's access token (payments scope, Idempotency-Key required) at POST /api/v1/miniapp/payment-intents; the amount is the backend's, and no person can reach it with a number of their own. The person sees the app's name and the amount in the host's own UI (/api/v1/miniapp/payments/{id}) and confirms from one of their accounts, which places a payment_auth hold on core-server through modules/bank's Ledger, after asking the ledger that the account is theirs. Automatic intents capture at once; manual ones are captured or canceled by the app within seven days (cancel releases the hold). Capture goes to the payout account the operator sets in review (PUT /api/v1/miniapp/review/apps/{id}/payout) and the developer cannot change. Refunds follow a capture within ninety days and never total more than was paid (a SQL WHERE clause). Every state change is one guarded UPDATE; every ledger call's key is written before the call, and a timeout is retried with the same key by the reconcile job. Webhooks (payment_intent., refund.) enter an outbox in the same transaction as the change and are delivered by nexus.JobRunner jobs, signed HMAC-SHA256(secret, t.body), retried from a minute to a day; a rotated secret keeps signing beside the new one for 24 hours. A purge job clears expired launch codes, grants and tokens. Test-credential intents never reach core.

1.0.0 — 2026-10-22

Feature. The mini app platform: a developer console, operator review, a person's consent and launch, and the token exchange for a mini app's backend.

openpay's miniapp-platform (OMAS 1.0) ported into a module. An organisation that installs the app registers mini apps, edits them in draft, proves its origin with a manifest at /.well-known/gerege-miniapp.json (or openpay-miniapp.json), issues test/live credentials and submits for review; a secret is shown once and only its hash is kept. Only the MINIAPP_OPERATOR_TENANT_ID organisation decides reviews (empty means closed); suspension revokes every token the app holds at once. A person sees published apps, consents and launches — each launch mints a 60-second, single-use launch code (single use enforced in SQL, UPDATE … WHERE used_at IS NULL). The mini app's backend redeems it with client credentials at /api/v1/miniapp/oauth/token for an access token and a rotating refresh token (a replayed code or refresh token revokes the whole chain), and /api/v1/miniapp/me names the person by a per-app pseudonymous subject, never the platform's id. Next: payment intents (through modules/bank to core-server holds), settlements, webhook delivery, the layout engine.