The gap
The read endpoints under /api authenticate with a bearer token: app/Middleware/Auth.ts calls Auth.getUserFromToken(bearerToken) before falling back to the session cookie. docs/ingest.md documents a paginated read of a project's events using exactly that.
There is no way to obtain such a token. The only one that works is the personal access token carried inside the session cookie, which is issued at sign-in and is not something anybody should be copying into a script.
So the read API is real, reachable and undocumented-by-necessity: docs/api.md currently tells readers to treat it as internal, which is the honest position but not the one we want.
Why it matters
Every "get my data out" path today is a file or an email: exports, scheduled delivery, share links. Those cover the reporting cases, and none of them covers "our warehouse pulls yesterday's totals every morning", which is the first thing a technical customer asks for after they have been sending events for a month.
It also removes the last reason the ingest key is asymmetric. The write credential is public by design and can only append. A read credential has to be the opposite: secret, revocable, and scoped to one project.
Shape
- A token per project, created and revoked from project settings, shown once at creation.
- Scoped to one project and to reading. A token for project A must not resolve anything in project B, and must not be able to write events, which the ingest key already does.
- Revocation takes effect on the next request, the way share link revocation does.
- Last-used recorded, so a token nobody uses can be found and removed.
- Rate limited per token rather than per account.
Once it exists, docs/api.md gets a real reference generated from that surface alone, rather than the 688-path buddy generate:openapi output that mostly documents framework defaults.
Notes
Found while writing the documentation site (#20). See the API page for what it currently says.