Authentication
How a partner API request presents an API key, and what the server does with it.
Every partner API endpoint requires an API key. API keys are issued by
Pentr. A key has the form pentr_ followed by 48 lowercase hexadecimal
characters, 54 characters in all.
Presenting the key¶
The server reads the key from one of three headers:
curl -H "X-API-Key: $PENTR_API_KEY" https://<pentr-host>/api/v1/usage
curl -H "Authorization: Bearer $PENTR_API_KEY" https://<pentr-host>/api/v1/usage
curl -H "Authorization: ApiKey $PENTR_API_KEY" https://<pentr-host>/api/v1/usage
The Authorization header is examined first. If its value begins with
Bearer or ApiKey — case-sensitive, followed by a space — the rest of the
value, with surrounding whitespace removed, is the key. Otherwise the key is read
from X-API-Key. A recognised scheme is final: once Authorization carries
Bearer or ApiKey, X-API-Key is not read, even if the Authorization
key is empty or wrong.
| Headers sent | Result |
|---|---|
Authorization: Bearer <valid key> |
Authenticated |
Authorization: ApiKey <valid key> |
Authenticated |
X-API-Key: <valid key> alone |
Authenticated |
Authorization: Bearer <wrong key> and X-API-Key: <valid key> |
401 Invalid or inactive API key |
Authorization: Bearer (empty) and X-API-Key: <valid key> |
401 API key required |
Authorization: bearer <valid key> (lowercase) alone |
401 API key required |
Authorization: bearer <valid key> and X-API-Key: <valid key> |
Authenticated, from X-API-Key |
Authorization: Basic … and X-API-Key: <valid key> |
Authenticated, from X-API-Key |
| No key in any header | 401 API key required |
Send the key in one header only.
Failures¶
A request that does not authenticate is answered 401 with one of three
bodies.
detail |
When |
|---|---|
API key required |
No key was found, or the key read was empty |
Invalid or inactive API key |
The key matches no active key |
Organization not found |
The key matches, but its organization no longer exists |
{"detail": "Invalid or inactive API key"}
The 401 carries no WWW-Authenticate header. On an existing route,
authentication runs before the request's parameters and fields are validated, so
a request with no key is answered 401 even when a path parameter or a body
field is invalid. A body that is not valid JSON is the exception: it is answered
422 before any key is read. A path
that matches no route, or a method the route does not have, is answered 404 or
405 before any key is read.
What the server stores about a key¶
Besides a bcrypt hash of the key and a name, the stored key has two fields that bear on requests:
is_active— only active keys are compared with the key presented. An inactive key is answered401 Invalid or inactive API key, the same body as a key that was never issued.last_used_at— set to the current UTC time each time the key authenticates a request.
No partner endpoint returns either field. A key has no expiry date and no scopes: an active key authorizes every partner endpoint for its organization.
Whom a call acts for¶
A key belongs to one organization, and a call made with it acts for that organization: it reads and writes the organization's assets, engagements, schedules and webhooks. The asset, engagement, report and schedule endpoints act as a user of that organization, and an organization has one user, its owner. An engagement or schedule created with a key is recorded as created by the owner. An asset created with a key has no creator recorded, and a verification attempt made with a key has no acting user recorded.
For an organization with no user, those endpoints answer
500 No user found for organization. Three groups of endpoints do not look up a
user and are unaffected: GET /api/v1/usage, POST /api/v1/schedules/preview
and the webhook endpoints.
Signing keys are a different thing¶
The route /api/keys on the console's own UI API manages the
evidence locker's signing keys, not API keys.
Last updated 2026-09-24