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.