Auth context shape
When the server accepts your connection, it resolves a context that looks like:brandId is the most important field — every read filters by it, every write rejects cross-brand IDs against it.
Permissions
Every key carries a scope list, chosen when you mint it:read is the default. Calling a write tool with a read-only key returns a 403 with Insufficient scope — the check runs before the tool’s own logic, so nothing is written and no lookup happens.
Give an integration the narrowest scope that does its job. An agent that reports on visibility does not need write, and a read-only key cannot archive your tracked questions or spend plan capacity if it leaks.
Keys created before permissions were enforced carry
read + write, because that is the access they have actually had. Check the Permissions column in Settings → API Keys and re-mint any that should be read-only.Transport options
stdio (local clients)
Used by Claude Desktop, Cursor, and other local MCP clients. Brand and user come from environment variables you set in your client config:Streamable HTTP (remote agents)
Used by hosted agents and remote integrations. Auth is an API key in theAuthorization header:
(brand, user, api_key) tuple. The resolved API key id is recorded on the auth context so every write is traceable back to a specific key.
Brand scoping
Every read query filters by your auth context’sbrandId at the database layer. Every write tool that accepts an ID looks up that ID and rejects with a 403 if it belongs to a different brand. There’s no MCP-side workaround — the same guard applies whether the client is Claude Desktop, a hosted agent, or any other caller.
Example: approve_ticket({ ticket_code: "OTHER-001" }) resolves the ticket, sees it belongs to a different brand, and rejects with a 403. The audit log records the failed attempt.
API key lifecycle
Create API keys in the Citable web app under Settings → API Keys. Each key:- Belongs to one user (the creator) and one brand
- Cannot be used to act on any other brand
- Can be revoked anytime — revoked keys fail auth immediately
- Records
last_used_aton every successful call
Rate limits
Calls are limited to 60 per minute per API key (requests that arrive before auth resolves are bucketed by client IP instead). The limit is currently flat — it does not vary by plan tier, and it counts reads and writes together. Response headers tell you where you stand:RateLimit-Reset is seconds until the window rolls over. Exceeding the limit returns a JSON-RPC error with Rate limit exceeded. If you need a higher limit for a specific integration, get in touch from your account.
Rate limiting is throughput protection, not an authorization control. Use the
read scope to stop an integration from writing.