A tenant is the scope your data lives in. An API token authorises calls against exactly one tenant. You will need at least one of each before the public API will return anything.
A tenant is either:
groupCode (e.g. KT50 for East Kent Walking Group), orareaCode (e.g. KT for Kent Area)The kind matters for the API path: groups are queried as /api/groups/{code}/members, areas as /api/areas/{code}/members.

The dashboard sidebar lists every tenant you own. Click + New to add one, or click an existing code (KT, KT06) to open its detail pane.
In the dashboard sidebar, click + New next to "Tenants" and fill in:
KT50, KT, NS03)group or areaEast Kent Walking Group)Hit Create. The tenant appears in the sidebar; click it to open the detail pane, which has three tabs: Members, Generate, and Tokens.
Tenant codes are unique across the whole mock server, not just per operator. If KT50 has already been claimed by someone else, you'll get a 409 conflict on create. Pick a different code, or request a transfer.
This is intentional: the public API path /api/groups/{code}/members would otherwise be ambiguous, and downstream consumers wouldn't know whose dataset they were calling against.
Open the tenant's Tokens tab. Each token authorises calls against this tenant only — there is no concept of a multi-tenant or operator-wide token.

The Tokens tab inside a tenant's detail pane. Type a label, hit Generate token, and copy the revealed plaintext immediately — it's only shown once.
Type a human-readable label (the input placeholder shows MailMan - NS03 dev as one example; NGX production sync and James local laptop are equally fine — the label is just for your own bookkeeping) and hit Generate token.
A panel reveals the full token string with a Copy button. Copy it now. The plaintext is shown exactly once; on the next page load all you'll see is the 16-character prefix in the table below. The server stores only a SHA-256 hash, so nobody — not even the root operator — can retrieve the original after generation.
rsm_<tenantCode>_<48-hex-random>
The tenant code is baked into the plaintext. That makes the prefix self-identifying: skim a log, see rsm_KT50_…, and you immediately know which tenant the request was scoped to. The 48-hex suffix is generated from crypto.randomBytes(24), so the keyspace is 192 bits.
A password manager, a GitHub Actions secret, a Fly.io secret, or whatever your team uses — anywhere except a checked-in file.
In the same Tokens table, every non-revoked row has a Revoke button. Revocation is immediate and one-way: the token row stays in the table for audit, but any call presenting that token will fail with 401.
If you lose a token, revoke it and generate a new one. There is no recovery path.
KT50 cannot read NS03; the API returns 401.tenantCode (data) or ownerOperator (admin). Operators cannot see each other's tenants, tokens, members, or consent events.