Authentication
Three auth modes, three headers, three audiences. Pick the right one for the route.
The bichito API uses three independent auth modes. Each mode fits a different use case; the route you call dictates which one is accepted.
| Mode | Header | Audience | Issued by |
|---|---|---|---|
| JWT | Authorization: Bearer <jwt> | Dashboard / your own user | POST /auth/login, POST /auth/signup, OAuth flow |
| Project API key | X-API-Key: sk_… | The widget (anyone in your end-user's browser) | Honeycomb settings → Additional API keys |
| MCP token | X-MCP-Token: mcp_… | AI assistants (Claude Code, Cursor, …) | /me/settings/mcp |
JWT (Bearer)
Used by the dashboard and any tool acting on your behalf. Signed with HS256, valid for 30 days. Carries your user id; the API maps that to your accessible hives.
Acquire one:
POST /api/v1/auth/login
content-type: application/json
{ "email": "you@example.com", "password": "supersecret" }
→ 200
{ "access_token": "eyJ…", "token_type": "bearer" }
Then send it on every authenticated request:
GET /api/v1/teams
authorization: Bearer eyJ…
OAuth (Google / GitHub) returns the same shape via the redirect flow — /api/v1/auth/oauth/{provider}/callback ends with the token in a redirect URL.
There's no refresh token; when the JWT expires, log in again. Sign-out is client-side (clear local storage) — there's no server-side blocklist of issued tokens.
Project API key (X-API-Key)
The publishable widget key. Created per honeycomb. Only authorises ingestion to that specific honeycomb:
POST /api/v1/bugs
x-api-key: sk_3vH…
content-type: application/json
{ "title": "...", "description": "..." }
The widget pastes this key into the user's HTML — see the transparency note for why that's intentional and what protections kick in (rate limit, no read access, no settings access, revocable).
Some endpoints accept this key too — for example, GET /api/v1/widget/info (used by the widget itself to know whether to show the "Powered by" badge). They're all listed in Ingest.
MCP token (X-MCP-Token)
Per-user, scoped, designed for AI assistants. Created from /me/settings/mcp:
POST /api/v1/mcp/bugs/123/resolve
x-mcp-token: mcp_a1b2c3…
Each MCP token carries a scope set chosen at mint time (see MCP → Scopes). The route checks the required scope and returns 403 if missing.
Different concept from the project API key — the project key is per-honeycomb and publishable; the MCP token is per-user and confidential.
What if I send no auth?
Most endpoints return 401. Public endpoints (the ingest endpoint, OAuth start URLs, /health) don't need any auth.
Mixing auth headers
Don't send more than one of Authorization, X-API-Key, X-MCP-Token on the same request. The route picks the relevant one and ignores the rest, but mixing is a sign of a confused client and risks bugs when the route shape changes.
Token storage on the client
Standard browser advice:
- JWT in
localStorage(the dashboard does this). Trade-off: survives a tab close, vulnerable to XSS if you have it. We don't have an XSS surface onbichito.dev, but if you're embedding the dashboard somewhere with one, audit accordingly. - API key in env vars on your build, templated into the snippet. The widget then reads it from
data-key. - MCP token in your AI tool's MCP config (e.g.
~/.config/claude/...).
Never commit a JWT or MCP token to a public repo. The widget API key is publishable so committing it is fine — but keep it in env vars anyway for rotation hygiene.