Skip to content

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).

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