Reference
Authentication
One bearer key per integration. The key is the control; everything else is derived from it.
The header#
Every request carries the key in the Authorization header, in the documented format:
Authorization: Bearer tfk_live_<key id>.<secret>The key id is public and appears in the dashboard and in error logs. The secret is shown once, at mint or rotation, and is stored only as a peppered hash. If you lose it, rotate the key; there is no way to reveal it.
Server-side only
A key embedded in a browser, a mobile binary, or a public repository is exposed by construction, and rotating it is the only remedy.Scopes#
A key carries a set of scopes fixed at mint time. A route declares the scope it needs; a key without it is refused with 403 forbidden_scope, and the handler never runs.
| Scope | Served today |
|---|---|
catalog:read | Yes. The products a granted clinic can order. |
catalog:read:pricing | Not yet. The catalogue reports priced as a boolean, never an amount. |
patients:read, patients:read:phi, patients:write | Reserved. No patient route exists yet. |
orders:read, orders:read:clinical, orders:write | Reserved. No order route exists yet. |
webhooks:manage | Reserved. No webhook route exists yet. |
Clinics#
A key is granted one or more clinics. Every read is scoped to them on the server, and there is no clinic parameter to send. A clinic partner is bound to exactly one clinic at creation; a pharmacy, lab or marketing partner is granted the clinics it serves.
A credential with no live grant is refused with 403 forbidden_clinic, even though the key itself verified.
Why a 503 is not a key problem#
The gate distinguishes two very different failures:
- The key did not verify. Refused with
401 unauthorized. Do not retry; check the key, or mint a new one. - We could not check the key. Refused with
503 service_unavailableandRetry-After: 5. Retry with the same key. Treating this as a bad key is how a working integration deletes its own credential.
Rotation#
A key is replaced in one action: the old key is revoked and a new one issued in the same statement, so there is never a moment when two keys are live. Anything still using the old key fails immediately, which is the point. Coordinate the swap with your deploy.
IP allowlisting#
A key can be restricted to a set of static egress ranges. There are three modes, and the default is off:
- Off. No check.
- Alert. Every request is checked and the result recorded, and nothing is refused. This is how the first month works: we learn your addresses before we start rejecting.
- Enforce. A request from an address outside every listed range is refused with
403 forbidden_ip.
Send us your static egress ranges when they are stable. We add them, run alert mode until the recorded traffic is understood, then enforce. A new range has to be added before you switch to it, and it takes effect on the next request.
An address we cannot determine is treated as unknown, and in enforce mode that also fails closed. If you sit behind a proxy that strips the client address, tell us before enforcing.
Next: the catalogue, or every refusal in one table.