Agents
An agent is the unit of scope and persona in Chamade. It ties together one provider, the keys that drive it, and the platform accounts it can reach. Most users have exactly one and never think about it.
What an agent is
When you connect a platform and mint a key, Chamade groups them under an agent: a named persona that pairs a credential (and optionally an LLM provider) with the platform accounts it's allowed to see and act on. An agent driven by a key can only touch the accounts attached to it — nothing else.
Throughout these docs, agent always means this Chamade unit. The thing on your side — your code, your AI loop, your MCP client — is your app.
The mental model
One agent holds:
- One provider config, or none — OpenAI, Anthropic, Gemini, Foundry, Bedrock, Mistral, or Custom (Responses). This is set only in inline-provider mode (below); in BYO-runtime mode there's no provider.
- N bearers —
chmd_*API keys and/or MCP OAuth clients (claude.ai Custom Connector, Cursor, …). They inherit the agent's scope. - N platform accounts — a Microsoft Teams connection, a Discord bot, a SIP number, a Google account, … Each must belong to ≥1 agent to receive events.
Every inbound event (a DM, an incoming call, a transcript line) is scoped to the agent that owns the account it arrived on, and dispatched once to that agent. Manage agents at dashboard → Agents.
Two ways to drive an agent
Pick per agent. They're not exclusive — an agent can expose keys and carry a provider — but most use one or the other.
Bring your own runtime
Your app holds the agent's chmd_* key (or an MCP OAuth bearer) and does the thinking. Chamade is a pure gateway: your app reads events and calls back to act.
Inline provider
You attach an LLM provider to the agent. Chamade calls it on every scoped event, parses the reply's XML action tags (<say>, <write>, <dm>, …) and fans them out. No runtime to host — Chamade drives your model.
- Configure in dashboard → Agents → Provider.
- Catalog and per-provider setup: see the AI providers section in the sidebar.
- Best when you don't want to host an agent loop at all.
Both models handle chat turn-based and cheap. For voice, you separately choose hosted STT/TTS (add a provider key, Chamade runs the pipeline) or raw-PCM BYO-audio on the call WebSocket — independent of how the agent is driven.
Keys & authorization
There are two kinds of chmd_* key, and the distinction is the whole point: the key an agent holds must not be able to reconfigure the account it runs under.
- Agent key — a member of ≥1 agent, scoped to that agent's accounts. The runtime credential: joins calls, sends DMs, reads the inbox. It is 403 on the entire config surface — it cannot create agents, connect platforms, or mint keys.
- Settings key — a member of no agent, so it acts on nothing at runtime. The admin credential: it configures the account by API (create agents, attach platforms, set providers). Created, listed, and revoked from the dashboard only — a bearer can never mint one.
These map onto three authorization tiers:
| Tier | Who can call it | Surface |
|---|---|---|
| Control-plane | Any valid key (agent or settings) | Calls, DMs, inbox, files — /api/call/*, /api/dm/*, /api/inbox |
| Config | Dashboard session or a settings key (agent keys → 403) | Everything under /api/agents/*, voice providers, platform connections, bot tokens, SIP, key mint, data export |
| Account-owner | Dashboard session only (no bearer at all) | Delete account, change email / password, manage settings keys |
OAuth-based platform connects (Microsoft, Google, Discord, NC Talk, and the Gemini / Foundry providers) always require a browser consent on top, whatever key you hold.
The default agent
Exactly one of your agents carries the Default badge. Its job is to receive any platform you connect next, automatically — so a casual user who connects Teams, Discord, and one key never has to think about scope.
- Lazy-created as "Default" the first time you mint a key or authorize an MCP OAuth client; it absorbs every platform you'd connected before it existed, in one shot.
- Every newly connected platform auto-attaches here.
- Un-deletable, but renameable. Move the badge to another agent with Set as default.
If you stick with one key + the default, agents are invisible: that key sees every platform, exactly like single-bucket Chamade.
When to create more
- One persona per platform or role — a "support" agent on Discord + Telegram, an "ops" agent on Slack + Teams + SIP. Separate scope means no cross-replies; each can have its own system prompt and even its own provider.
- Multi-tenancy — two Microsoft 365 tenants, or two Google accounts, on one Chamade account: a dedicated agent per tenant keeps scope and memory separate.
- OAuth clients with selective reach — let claude.ai reach your support platforms but not your work Teams. The consent screen's platform picker becomes that client's agent scope.
Scope: what an agent sees
A bearer (key or OAuth client) can only see and act on accounts that share an agent with it. Concretely:
chamade_inboxandGET /api/callsreturn only conversations and calls whose account is in one of the bearer's agents.chamade_dm_chat/chamade_call_chat/chamade_call_say/chamade_call_typing(and their REST forms) return HTTP 403 "no agent for this account" when the target account is outside scope.- For default-only users (one agent, default badge), the scope check always passes.
The no-orphan rule: every bearer and every platform account must belong to ≥1 agent, so a credential never silently sees nothing and inbound events are never dropped. You can't delete an agent if it would orphan a member, and you can't detach the last agent of a key or account — move it elsewhere first. The default is un-deletable, so there's always a safe fallback.
REST API
Every agent-shaping endpoint lives under /api/agents/* and is config tier: a dashboard session or a settings key only — agent keys, MCP OAuth, and identity tokens get 403. Full reference: REST API → Agents.
