Authentication
Authenticate every request with your API key as a bearer token:
Authorization: Bearer sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Key format
Keys are prefixed sk_ (secret key) followed by a random secret. A key operates
on your real organization data.
Only a hash of the key is ever stored. The raw key is shown once when you create it in Settings ▸ API Integration. If you lose it, revoke it and mint a new one — it cannot be recovered.
A newly created key takes up to about a minute to activate. During that brief
window requests with the new key may return a 403 (the key is registered but not yet
propagated). This is expected — wait ~1 minute and retry. (The 403 body is a raw AWS
authorizer message, not a fixed string — branch on the status code, not the body. See
Errors.)
This API is for server-to-server use. It does not support browser (CORS) requests, and your secret key must never be embedded in client-side code.
Access
Every API key grants full access to all endpoints in this API. There are no per-entity scopes to choose — a key can read and write every organization entity (employees, sectors, cost centers, job roles, groups, products) and read every transaction and training record (withdrawals, returns, exchanges, training records). A key only ever sees your own organization's data.
Webhook endpoints are managed in the web console (Settings ▸ API Integration), not over this API.
Expiry (optional)
A key may be created with an optional expiry date. After that moment the key is
rejected exactly like a revoked or invalid key — a 403 (see Errors). A
key created without an expiry never expires on its own. Choose an expiry when minting
the key in the console (Settings ▸ API Integration); it cannot be extended afterward,
so to keep an integration running past the date, mint a replacement before it lapses
(see Revoking for the rotate-then-revoke pattern).
Revoking
Revoke a key from the console. Revocation takes effect within about a minute — authentication results are briefly cached, so a revoked key may keep working for up to ~60 seconds before it is rejected. Plan for this small window when rotating a compromised key. Existing keys are never auto-rotated — rotate by minting a replacement, updating your integration, then revoking the old key.
IP allow-listing (optional)
A key may be restricted to a set of IPv4 CIDR ranges (e.g. 203.0.113.0/24) at
creation time. Requests from outside those ranges are rejected. Only IPv4 CIDRs are
accepted — IPv6 ranges are rejected when the key is created, so call from an IPv4
source (or proxy through one) if you use an allow-list.
Rate limits
Rate limits apply per organization, not per key — all of your keys share one
throttle + quota bucket. Holding more keys does not raise your limit. When you exceed
the limit, requests return 429 with the body {"message":"Too Many Requests"}. A
Retry-After header is not guaranteed on a 429 (the burst-rate throttle does not
send one), so do not depend on it — back off and retry with your own exponential
backoff. The default ceilings are generous (well above normal integration traffic);
contact your provider if you have a legitimate need for more.