For engineers
How the API is built
For engineers: how a call on /api/v1 becomes a person, how keys are stored and checked, how the fence works, and what the server does when a key ends.
Why this page exists
These docs are for people calling the API. This page is for the engineers who want to know what stands behind it before they trust it with a company's figures. It is drawn from the engineering notes the Panlytics team keeps beside the code, so it changes when the code does.
A key acts as the person who made it
A call to /api/v1 carries a key in the Authorization header. The server
turns the key into the person who made it, in the company it was made in, and
from then on the call goes through the same permission checks that person's
own pages go through. There is no second permission system for the API to
drift away from the app: when a role change takes a shop away from a person,
their key loses that shop on the next call.
A key never reaches more than its maker. It can reach less: the fence, below, takes some abilities away from every key.
What a key is
A key reads pan_<public id>_<secret>. The public id names the key, and the
secret is 32 random bytes. The pan_ prefix lets secret scanners, such as
GitHub's, recognise a key that leaked into code.
The key is shown once, in the dialog that made it. Panlytics stores only a keyed hash of it, never the key, and never writes the key to a log. A copy of the database is not a copy of anybody's keys. The secret is compared in constant time.
Every call checks everything again
Nothing about a key is trusted from an earlier call. Each call checks the
key's shape, its secret, that it is not revoked and not past its end, that
developer mode is still on for the company, that the maker's account is still
active, and that the maker is still in the company with the ability to make
keys. Any failure is the same 401 api.key_refused, so a caller cannot learn
which check failed. The reason is recorded in Panlytics'
security log.
A malformed Authorization header is refused before the database is asked
anything.
Hard expiry
A key works for at most the company's period, 1 to 5 hours, which the CEO sets. Five hours is a ceiling the database itself enforces, whatever the code says. Shortening the period shortens every live key at once. There is no refresh: when a key ends, a person makes a new one.
A key also ends early when its maker or the CEO revokes it, when the CEO switches developer mode off (every key in the company ends), and when its maker signs out everywhere, changes their password, or is removed, banned or switched off. Revoking a key also ends any AI session it signed in.
The fence
Some things no key may do, whatever its maker's role holds: manage people, manage roles, read the audit trail, make or manage keys and the developer settings, and change the AI settings. A key made by the CEO is fenced the same way, so the CEO's own powers are never available through a key. Pages that are only for a person, such as personal security, the account page and both Developer pages, do not exist for a key. A key may ask the AI agents, and approving what they propose stays with a person in the app.
A key cannot leak into the wrong request
A key's identity lives only as long as the request that carried it. Code that
runs outside that request sees nobody, and the attempt is logged. A key sent
to any address outside /api/v1 is ignored. A call with a key can never
borrow a browser's session, and a key never counts as a recent sign-in, so an
action that asks a person to confirm who they are always refuses a key.
Limits
Each key may make 120 calls a minute. Past that, calls answer
429 api.rate_limited with a Retry-After header. Only calls with a valid
key count, so knowing a key's public id without its secret cannot spend
somebody else's minute. An address that sends 30 wrong keys in a minute is
answered 429 for the rest of that minute. A person may hold at most five
live keys at once.
What is recorded
Every change a key makes is written to the company's audit trail as its maker, marked as made with an AI key. Making and revoking keys, and changing the developer settings, are recorded too. The key itself appears nowhere: not in the audit trail, not in the logs, not in product analytics.
Answers
Every answer carries Cache-Control: no-store. The API sends no CORS headers,
so a web page in a browser cannot call it with a key: call it from a server, a
script or an agent. A refusal is JSON with a stable code, and a fault on our
side carries a requestId to quote when you report it.