Skip to content

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:read scope (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_scope with required_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 (not webhook.test — test sends never appear in this API; type=webhook.test is 400 test_event_not_listed); created_gte — an RFC 3339 time; limit 1–100 (default 50).
  • Paging: after = the previous page's next_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 after older than that, or not this app's and environment's, is 400 unknown_cursor — start again with created_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"
  1. Page until next_cursor is null; handle every event with the same idempotent code as the webhooks (dedupe on the event id — skip what already came by webhook) (Idempotent handlers).
  2. Store the last event's id; when next_cursor is null, use that id as after next time.
  3. 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.