Authentication
Every authenticated route takes an X-API-Key header:
X-API-Key: <your live or sandbox key>Each tenant has two API keys — api_key_live and api_key_sandbox — issued at onboarding.
Whichever one you send determines the mode (live or sandbox) for that request; there is no
separate mode field to set. All data — QRIS codes, transactions, webhook config — is scoped to
the tenant the key resolves to, and further scoped to that key’s mode: a live key never sees
sandbox transactions or vice versa.
This is a separate credential from secret_key_live/secret_key_sandbox — that pair only signs
outbound webhook deliveries and authenticates the inbound App Listener webhook (see below); it is
never accepted as X-API-Key.
Missing or unrecognized key → 401 INVALID_API_KEY (see Errors).
Rotating keys
Section titled “Rotating keys”POST /v1/regenerate-secret and POST /v1/regenerate-api-key each rotate only the credential for
the mode you authenticated with — the other mode’s key is untouched. The old key stops working
immediately, so update anything still using it (an App Listener holding secret_key_*, your own
integration holding api_key_*) before its next call. Both are rate-limited tighter than other
writes (10/min), since this invalidates your own credential — not something a legitimate
integration needs to do rapidly.
// POST /v1/regenerate-secret{ "success": true, "message": "Secret key regenerated", "data": { "mode": "live", "secret_key": "live_..." } }// POST /v1/regenerate-api-key{ "success": true, "message": "API key regenerated", "data": { "mode": "live", "api_key": "live_..." } }The inbound webhook (POST /v1/webhook/payment-status) is the one exception: it’s called by
the App Listener, not by your backend, and authenticates differently — see
its contract in Webhooks.
Continue to Flow Overview →