Authentication
How applications authenticate with an API key + secret, how credentials rotate and revoke, and how admin sessions differ.
The service has two parallel authentication mechanisms. As an integrating app you use the API key + secret. Operators using the dashboard use an admin session (covered in the admin guide).
App API key + secret
Every app-facing request carries two headers:
X-Api-Key: oi_test_xxx
X-Api-Secret: your-secretX-Api-Keyis a deterministic lookup handle. The service stores only its SHA-256 digest, so the key resolves a credential row without the raw value being recoverable.X-Api-Secretis verified against a bcrypt hash after the key resolves. Both must be valid.
The resolved credential determines the app and the mode
(TEST/LIVE). The mode is never read from the request body — a oi_test_… key
always operates on test data.
Send credentials only over HTTPS and only from your backend. Never embed the secret in a browser, mobile app, or anything a customer can inspect.
A credential is one of many
An app can hold more than one credential per mode. That is what makes rotation and revocation non-disruptive: each credential is an independent row that can expire or be revoked without touching the others.
| Credential status | Behaviour |
|---|---|
ACTIVE, no expiry | Authenticates normally. |
ACTIVE, with expiresAt | Still authenticates until the grace deadline passes (rotated-out key). |
REVOKED | Rejected immediately. |
Rotation (zero-downtime)
When a credential is rotated, a new one is issued immediately and the old
one is stamped with an expiresAt (a grace window, 24h by default). Both keys work
during the window, so you can roll your deployment over with no downtime:
- Operator rotates the credential; you receive the new key + secret (shown once).
- Deploy the new key alongside the old — both authenticate.
- Once all traffic uses the new key, let the old one expire (or have it revoked).
Revocation
Revoking a credential takes effect immediately — the next request with it is
rejected with UNAUTHORIZED. Use this if a secret may be compromised; issue a
replacement by rotating first if you need continuity.
Admin sessions (for reference)
Operators authenticate the dashboard with email + password and receive a bearer token:
Authorization: Bearer <session-token>Sensitive operations (rotating credentials, overriding the gateway, managing
users) additionally require a recent re-authentication — the operator
re-enters their password, and the action is written to the append-only audit log.
A stale session returns REAUTH_REQUIRED. App integrations never deal with this;
see the admin guide for details.
Authentication failures
errorCode | Meaning |
|---|---|
UNAUTHORIZED | Missing/invalid key or secret, or a revoked/expired credential. |
FORBIDDEN | Authenticated, but not permitted (admin RBAC). |
SESSION_EXPIRED | Admin session no longer valid. |
REAUTH_REQUIRED | Admin must re-authenticate before a sensitive action. |
All failures use the standard response envelope.