Every request to the public API must carry a tenant API key. There are no sessions, cookies or OAuth flows: the key is the credential.
owk_<key_id>_<secret>
owk — fixed prefix (useful for secret scanners).key_id — 16 hex characters, the public identifier of the key. It appears
in the Onwork admin UI (owk_1a2b3c4d…) and is not secret.secret — 64 hex characters. Onwork stores only a hash of it: the full key
is visible only once, at creation time.Primary header:
curl https://<host>/api/public/v1/users \
-H "X-Api-Key: owk_your_key_id_your_secret"
Alternative — standard Authorization header:
curl https://<host>/api/public/v1/users \
-H "Authorization: Bearer owk_your_key_id_your_secret"
Both are equivalent for authentication. The Bearer form additionally enables
idempotent retries, which are scoped by the
Authorization header.
All credential failures return the same generic 401 — the API does not reveal whether a key exists, is revoked, expired or malformed:
{ "error": { "code": "unauthorized", "message": "Invalid or missing API key", "status": 401 } }
A 403 forbidden is different: your key is valid but is missing a scope, or
a required platform module is not active for the company.
| Event | Effect |
|---|---|
| Created | Active immediately; plaintext shown once. |
Expired (expires_at) | Requests return 401. Set expirations for temporary integrations. |
| Disabled | Temporarily off (an admin can re-enable it). |
| Revoked | Permanently off — a revoked key can never be re-enabled. |
Rotation: create a new key with the same scopes, switch your integration, then revoke the old key. Keys are cheap; one integration = one key.
key_id part only.