Authentication
Two paths. Pick whichever matches the caller.
| Path | Header / cookie | Capability source |
|---|---|---|
| JWT cookie | Cookie: session=… (set on login) | Membership role on the active org |
| API key | Authorization: Bearer one_pk_… | The key's scopes array |
If both are present, the API key takes precedence.
JWT cookie
This is what the web app uses. Sign in at /login or via Google OAuth and the browser sends the cookie automatically. Capabilities follow the user's membership role on the currently-active org:
| Role | Public (metrics.read) | Privileged (people.view_cost, people.view_paygap) |
|---|---|---|
owner | ✅ | ✅ |
admin | ✅ | ✅ |
member | ✅ | ❌ |
contractor | ✅ | ❌ |
If you switch organisations in the app, every subsequent API call is scoped to the new org.
API keys
Mint at Settings → API Keys. Each key is scoped to one (user, org) pair and carries an explicit list of capabilities — the boxes you tick at creation time.
Format
one_pk_<32 chars of base64url>The first 8 chars after one_pk_ are the public prefix, shown in the UI so you can identify which key is which without ever revealing the secret. The full string is hashed (SHA-256) before storage; we cannot recover it once you've closed the dialog.
Use
curl -H "Authorization: Bearer one_pk_BzXzIiYbQ4UI…" \
"https://app.okavango.io/api/v1/metrics"Scope policy
When a request authenticates via API key, the key's scopes are the only grants beyond public. The underlying user's role is ignored — a key minted by an owner does not inherit owner privileges. This means:
- A
metrics.read-only key sees finance metrics, headcount, growth, diversity — but notcomp.*orpaygap.* - To grant compensation visibility, tick
people.view_costat creation - To grant pay-equity visibility, tick
people.view_paygapat creation - A key with all three boxes ticked still doesn't carry capabilities you didn't tick — there are no implicit grants
This matches how API keys work in most modern platforms (GitHub PATs, Stripe restricted keys): explicit scopes, never role inheritance.
Revoke
Settings → API Keys → Revoke. Revocation is immediate — every subsequent request returns 401 UNAUTHENTICATED from the next call onwards.
Errors
| Code | Status | Meaning |
|---|---|---|
UNAUTHENTICATED | 401 | No cookie, missing / malformed Authorization header, unknown or revoked key |
FORBIDDEN_CAPABILITY | 403 | Authenticated, but lacks the capability the metric requires |
See error catalogue for the full list.