Events API¶
How to fetch from your server what happened while webhooks were not getting through (an endpoint turned off, 5xx
answers during a deploy, all 8 attempts used up, a machine offline). Five minutes.
- You need: an API key with the
events:readscope (test or live — a key sees its own environment's events only).
1. The API¶
| Method | Path | Answer |
|---|---|---|
| GET | /api/v1/miniapp/events?type=&after=&created_gte=&limit= |
{"data": [Event], "next_cursor": "evt_…" \| null} — oldest first |
| GET | /api/v1/miniapp/events/{id} |
One event; another app's, another environment's or a purged one is 404 |
- Authentication: Basic (an API key), scope
events:read(missing:403 insufficient_scopewithrequired_scope). Rate limit 600 a minute. - The object is byte for byte the webhook body (the stored body that was signed:
id,type,created_at,livemode,app,data.object) — your webhook handler's code handles both. There are no signature headers: HTTPS and your key are the guarantee. - Filters:
type— one event type (notwebhook.test— test sends never appear in this API;type=webhook.testis400 test_event_not_listed);created_gte— an RFC 3339 time;limit1–100 (default 50). - Paging:
after= the previous page'snext_cursor(events after that one). Event ids are random, not in time order, so never make a cursor up — pass the server's.next_cursor: null= the end, for now. - Retention is 30 days (from when an event was created). An
afterolder than that, or not this app's and environment's, is400 unknown_cursor— start again withcreated_gte. - Events from the last ~2 seconds are not listed yet, so that a cursor never steps over an event still being committed. They appear on the next call.
- Events that had no endpoint at the time, or that no endpoint's filter took, are here too.
The error codes (invalid_type, test_event_not_listed, invalid_cursor, unknown_cursor, invalid_created_gte, invalid_limit) are on
Error codes.
2. A catch-up loop¶
Keep the id of the last event you handled and carry on from there:
# The first time: from the last day
curl -s -u "$GEREGE_CLIENT_ID:$GEREGE_CLIENT_SECRET" \
"https://geregeapp.mn/api/v1/miniapp/events?created_gte=2026-10-26T00:00:00Z&limit=100"
# Then: from the previous answer's next_cursor
curl -s -u "$GEREGE_CLIENT_ID:$GEREGE_CLIENT_SECRET" \
"https://geregeapp.mn/api/v1/miniapp/events?after=evt_…&limit=100"
# One type only
curl -s -u "$GEREGE_CLIENT_ID:$GEREGE_CLIENT_SECRET" \
"https://geregeapp.mn/api/v1/miniapp/events?type=payment_intent.requires_capture&created_gte=2026-10-26T00:00:00Z"
- Page until
next_cursorisnull; handle every event with the same idempotent code as the webhooks (dedupe on the eventid— skip what already came by webhook) (Idempotent handlers). - Store the last event's
id; whennext_cursorisnull, use thatidasafternext time. - Re-read the object (
GET /payment-intents/{id}) before fulfilling or closing an order — an event is a snapshot.
A system that uses no webhooks at all (a machine on a closed network, say) may poll this API every few seconds — stay within the rate limit.
3. Reconciling by objects¶
Besides events you can read the objects themselves — they are always the current truth:
# The intents of one order (reference = your order number)
curl -s -u "$GEREGE_CLIENT_ID:$GEREGE_CLIENT_SECRET" \
"https://geregeapp.mn/api/v1/miniapp/payment-intents?reference=order_8812"
# By status, paged (newest first; after = the last id of the previous page)
curl -s -u "$GEREGE_CLIENT_ID:$GEREGE_CLIENT_SECRET" \
"https://geregeapp.mn/api/v1/miniapp/payment-intents?status=succeeded&limit=100"
# Settlements (with a live key; cursor = the previous answer's next_cursor)
curl -s -u "$GEREGE_CLIENT_ID:$GEREGE_CLIENT_SECRET" \
"https://geregeapp.mn/api/v1/miniapp/settlements?limit=100"
A full daily reconciliation (statements, settlements) — Reconciliation, Statements. Paging and filter rules: API conventions.