Developer console — sandbox and analytics¶
Tools for testing your mini app without involving people, the operator or real money. All are workspace routes
(/api/v1/miniapp/dev/…): a member of the organisation that owns the app, signed in to Gerege App, with
miniapp.read to read and miniapp.manage to change. If the app belongs to another organisation, 404. The console
screens are being added to the core's shell, so the API below is the source of truth.
1. Webhook deliveries¶
| Method | Path | |
|---|---|---|
| GET | /dev/apps/{id}/webhook/deliveries?status=&environment=&limit=&cursor= |
The outbox, newest first |
| POST | /dev/apps/{id}/webhook/deliveries/{event_id}/retry |
«Дахин илгээх» ("Resend") → 202 |
| POST | /dev/apps/{id}/webhook/test |
Sends a signed webhook.test now and answers with the result |
- The list:
status∈pending,delivered,failed,skipped;environment∈test,live;limit1–100 (default 50); next pagecursor=<next_cursor>. Response{items, next_cursor?}, each row:
{ "id": "evt_…", "type": "payment_intent.succeeded", "environment": "live", "livemode": true,
"object_id": "pi_…", "status": "failed", "attempts": 8, "next_attempt_at": null,
"last_status_code": 500, "last_error": "HTTP 500", "delivered_at": null,
"created_at": "2026-10-22T09:00:00Z", "payload": { "id": "evt_…", "type": "…", "data": { "object": {} } } }
- Resend puts the event in the queue to be sent now and restarts the retry schedule (it is sent within a minute).
The body is the original — the signature has a new
t. - A test send is an ordinary outbox event: signed with the same signer and secret as a real event, it appears in
the list and, if it fails, is retried on the same schedule — if the test succeeds, the real path's test has
succeeded. Response:
{event_id, ok, status_code, error, duration_ms, delivery}. 20 per minute. - If no webhook URL is configured,
409 no_webhook.
The body of webhook.test (livemode is always false):
{ "id": "evt_…", "type": "webhook.test", "created_at": "…", "livemode": false, "app": "esim",
"data": { "object": { "message": "Gerege App webhook test", "object": "webhook_test" } } }
2. Test users¶
People who, besides the registered members, can open the app while it is draft / in_review.
| Method | Path | |
|---|---|---|
| GET | /dev/apps/{id}/test-users |
{items: [{user_id, note, added_by, created_at, name?, email?}], limit: 50} |
| POST | /dev/apps/{id}/test-users |
{"user_id"} or {"email"}, note? (≤ 120) → 201 (new) / 200 (already there) |
| DELETE | /dev/apps/{id}/test-users/{user_id} |
204 |
- A test user MUST be an active member of your organisation — by user id or by e-mail alike. A test user sees an
unreviewed app in their launcher as if it were approved; that is why a stranger cannot be added
(
404 user_not_found). The platform offers no way to look up outsiders by e-mail. - ≤ 50 per app (
409 too_many_test_users). None can be added to a suspended app (409 suspended). - A test user's launch code is a
testercode — it is redeemed with the test credential (mapp_test_…); no money moves.
3. Test payments¶
Create a test-mode intent without anyone opening the app, and play the host's confirmation from the console.
| Method | Path | |
|---|---|---|
| GET | /dev/apps/{id}/test-intents?cursor= |
Test intents, newest first, 20 at a time ({items, next_cursor?}) |
| POST | /dev/apps/{id}/test-intents |
The body is the payments API's (payments.md §2); Idempotency-Key optional |
| POST | /dev/apps/{id}/test-intents/{pi}/confirm |
Plays the host's «Төлөх» ("Pay") → the intent |
Confirmation follows the same rules as a person's confirmation — the amount decides: …01 → failed
(test_declined), …02 → canceled, manual → requires_capture, anything else → succeeded — and the
matching webhook goes into the outbox. Your backend continues with the returned intent through the payments API
(capture, cancel, refunds) using the test credential. The intent is the developer's (your own user_ref).
4. Analytics¶
| Method | Path | |
|---|---|---|
| GET | /dev/analytics?from=&to=&environment= |
Totals for each of the organisation's apps |
| GET | /dev/apps/{id}/analytics?from=&to=&environment= |
One app, per day |
from,to— Ulaanbaatar dates (YYYY-MM-DD), the last 30 days by default, ≤ 366 days.environment—live(default, the public) ortest(you and your test users).- Per-day rows / totals:
launches,unique_persons,consents_granted,consents_revoked,payments(status → count),captured_amount,refunded_amount(MNT). Days are split byAsia/Ulaanbaatar. - Only counts leave — never who, nor their
user_ref. - Each launch is kept for 400 days (the longest range, 366 days, is always whole); days before that count 0
launchesandunique_persons. - Consents are counted by each person's latest state (not an event log). Payments are the intents created in the range.
{ "app_id": "…", "slug": "esim", "from": "2026-09-23", "to": "2026-10-22", "timezone": "Asia/Ulaanbaatar",
"environment": "live", "currency": "MNT",
"totals": { "launches": 1204, "unique_persons": 388, "consents_granted": 41, "consents_revoked": 3,
"payments": { "succeeded": 97, "canceled": 12 }, "captured_amount": 4365000, "refunded_amount": 45000 },
"days": [ { "date": "2026-10-22", "launches": 51, "…": "…" } ] }