# 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.

Markdown twin of https://panlytics.net/developers/engineers/how-the-api-is-built (last updated 2026-10-25). Every page of the Panlytics developer docs is listed at https://panlytics.net/developers/llms.txt, and all of them are in one file at https://panlytics.net/developers/llms-full.txt. The site guide is https://panlytics.net/llms.txt.

## 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.
