The two host buckets are deliberately separate so SDK consumers never
have to think about JWT refresh, and so A2A / MCP clients can’t
accidentally pick up a user’s session cookie.
1. User JWT — web / mobile / desktop sessions
Issued by the main Gateway (api.beeos.ai, local :9080) on
login. Short-lived (typically 1 h, refreshed automatically by the
SDK / web client). Grants full access to the user’s own resources —
any operation the user can do from the web UI works with their JWT.
access_token; send it as Authorization: Bearer <jwt> to either Gateway (web traffic) or
openapi.beeos.ai (SDK traffic — it accepts the same JWT).
Use JWT only for interactive sessions. For backend cron jobs,
CI, or anything where you store the credential at rest, prefer an
oag_ key — JWTs expire and rotating refresh tokens through a
headless script is more friction than it’s worth.
2. oag_ — User API Key (SDK calls)
The credential SDK consumers actually pass through. Self-issued by
the user — no admin involvement.
oag_<64-hex>) once;
store it immediately. The Gateway only retains a SHA-256 hash.
Format
oag_<8 hex>) are stored separately as
key_prefix for safe display in lists / audit logs.
Authorization
oag_ User API Keys are user-scoped: each key is bound to one
owner and inherits full access to that owner’s resources. Every
route is gated by owner-ACL inside the handler — cross-tenant access
is denied uniformly, no matter which credential the caller used.
There is no per-route scope vocabulary any more. Earlier
revisions of this surface required scopes like agents:read /
tasks:write to be explicitly granted at key-creation time, with
missing scopes returning 403 insufficient_scope. That gate has
been removed (see the v1.1.0 changelog migration
below) — once authenticated, an oag_ key behaves like the
underlying user’s JWT for the purposes of authorization.
Migration from pre-v1.1.0 SDKs. If you used
createAPIKey(name, scopes, ...)
or POST /api/v1/api-keys with a scopes body field, drop the
argument — existing keys continue to work and grant full owner-level
access. The 403 insufficient_scope error is no longer emitted;
fold it into your generic 403 / forbidden handler.Listing / revoking
3. bak_ — Agent API Key (A2A JSON-RPC)
For external agents (or other organisations’ systems) calling
your agent over the A2A protocol on a2a.beeos.ai. Scoped to a
single agent — not a user. Each agent can hold at most 3 active
keys at a time.
Format
Listing / revoking
Why a separate prefix?
bak_ keys have no scope system and no user identity —
they’re an agent invocation token. A2A Gateway authenticates the
key, resolves the agent owner, then admits the request as that
agent. Mixing oag_ and bak_ namespaces would have made this
mapping ambiguous. See a2a.beeos.ai agent integration
contract.
4. MCP OAuth — Model Context Protocol clients
mcp.beeos.ai is a Model Context Protocol
host. MCP clients (Claude Desktop, Cursor, custom MCP servers) discover the
authorisation server via the standard MCP discovery doc and complete a
short OAuth 2.0 authorisation-code flow; the access token they receive
is only valid on mcp.beeos.ai — not on openapi.beeos.ai and not
on a2a.beeos.ai.
You shouldn’t normally hand-craft this flow — your MCP client
implements it. If you’re building a new MCP client, see the
MCP authentication spec.
Choosing between them
See Choosing a Protocol for the full side-by-side comparison (host, wire format, scope unit, streaming / async / push semantics, and worked examples). For deeper context on “which protocol surface should I be calling in the first place” seedocs/guides/choosing-a-protocol.md (P1-G,
planned).
Common mistakes
- Using
oag_ona2a.beeos.ai— A2A Gateway rejects it with401 unauthorized. The two host buckets share zero credential state by design. - Embedding
oag_keys in shipped mobile / desktop binaries — the user’s keys belong to the user, not your app. Web apps should authenticate the user (JWT) and your server mints a server-sideoag_for backend work. - Expecting per-route scope gating — the
agents:*/tasks:*/files:*/instances:*scope vocabulary was removed in v1.1.0.oag_keys are user-scoped; owner-ACL inside each handler is the sole authorization gate. - Expecting key rotation from openapi-gw — rotation /
revocation lives on the main Gateway (see endpoints above). The
OpenAPI Gateway is read-only for credential metadata; it does not
expose
POST /api-keys.
Where each credential is checked in code
See also
- Calling Agents — once you have a credential, here’s how to make the actual SDK calls
- OpenAPI contract — the routes you can hit with an
oag_key - A2A external integration — routes accepting
bak_ - ADR 001 — openapi-gateway as the sole OpenAPI implementer