Per-Agent API & MCP
Issue a key bound to a single agent. The /v1/agents/:agentId REST surface, the agent MCP endpoint, the agent scope namespace, the tool catalog, and the isolation guarantee.
What a per-agent key is
A per-agent key (ca_agt_…) is bound server-side to exactly one agent. It can read and (if you grant it) drive that one agent — and nothing else. It cannot enumerate other agents, cannot reach your contacts, billing, inboxes, integrations, or webhooks, and cannot mint keys.
This is ideal for handing a single agent to a third party, an embedded app, or a CI job without exposing the rest of your account.
The binding lives on the hashed key row in our database, not in the URL. Changing the :agentId in a request path does not widen a per-agent key — it just gets a 403.
Where to mint the key
Per-agent keys are minted from the agent's own settings, not the account API-keys page:
- Dashboard: open the agent → Settings → API & MCP → New key. Copy the secret immediately (shown once).
- REST:
POST /v1/agents/:agentId/api-keys(admin session or account key, depending on your org policy).
curl -X POST https://api.customagents.io/v1/agents/agent_abc123/api-keys \
-H "Authorization: Bearer ca_acct_YOUR_ADMIN_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Embed: support widget",
"scopes": ["agent:config:read", "agent:conversations:read"]
}'{
"id": "key_def456",
"name": "Embed: support widget",
"keyType": "agent",
"agentId": "agent_abc123",
"keyPrefix": "ca_agt_",
"scopes": ["agent:config:read", "agent:conversations:read"],
"key": "ca_agt_…", // shown ONCE
"createdAt": "2026-06-17T10:30:00Z"
}The keyType and agentId are forced server-side — you cannot widen a key by passing a different agent, an account scope, *, or write:api_keys. A per-agent key is also denied this route: a key cannot mint another key.
Agent scopes
A per-agent key holds scopes only from the agent: namespace. The default (when scopes are omitted) is read-only.
| Scope | Grants | Default |
|---|---|---|
agent:config:read | Read this agent's configuration | ✓ |
agent:conversations:read | Read this agent's conversations | ✓ |
agent:activity:read | Read this agent's activity | ✓ |
agent:config:write | Update this agent's configuration | opt-in |
agent:trigger | Trigger this agent via chat — money-spending | opt-in |
agent:trigger is separated from the reads so a read-only key can never spend credits. Grant it only when the key needs to drive the agent.
REST surface
A ca_agt_ key can reach only /v1/agents/<its-own-agentId>/**:
| Method & path | Required scope |
|---|---|
GET /v1/agents/:id | agent:config:read |
PATCH /v1/agents/:id | agent:config:write |
GET /v1/agents/:id/conversations | agent:conversations:read |
GET /v1/agents/:id/activity | agent:activity:read |
POST /v1/agents/:id/chat | agent:trigger |
# Read this agent's config
curl https://api.customagents.io/v1/agents/agent_abc123 \
-H "Authorization: Bearer ca_agt_YOUR_KEY"
# Trigger the agent (requires agent:trigger)
curl -X POST https://api.customagents.io/v1/agents/agent_abc123/chat \
-H "Authorization: Bearer ca_agt_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{ "message": "Summarize today's open threads" }'Per-agent MCP
The agent also exposes a dedicated MCP endpoint so an IDE can work with just that one agent:
Per-agent MCP endpoint: https://api.customagents.io/v1/agents/<AGENT_ID>/mcpAuthenticate with the ca_agt_ key for that agent. Configuration is identical to the account MCP setup — only the URL and key differ. For Claude Code:
claude mcp add --transport http my-agent \
https://api.customagents.io/v1/agents/agent_abc123/mcp \
--header "Authorization: Bearer ca_agt_YOUR_KEY"Agent tool catalog
The per-agent MCP server registers only agent-scoped tools — there is no list_agents and no cross-agent or account tool. Every query is hard-bound to { organizationId, agentId }. Destructive tools prompt for confirmation in the IDE.
| Tool | Required scope | Destructive |
|---|---|---|
get_agent_config | agent:config:read | — |
get_agent_conversations | agent:conversations:read | — |
get_agent_activity | agent:activity:read | — |
update_agent_config | agent:config:write | Yes |
trigger_agent | agent:trigger | Yes |
Isolation guarantee
A per-agent key (ca_agt_…) is structurally confined to its one agent. Concretely, it returns 403 on:
- The account MCP endpoint
POST /v1/mcp. - Any other agent:
GET /v1/agents/<otherId>/**and/v1/agents/<otherId>/mcp. - The agents collection
GET/POST /v1/agents. - Every account router: contacts, messages, inboxes, domains, webhooks, integrations, subscriptions, payment methods, team, notifications, and
api_keys. - Bulk endpoints such as account-wide
/activityand/drafts.
There is no cross-agent or account tool in the per-agent MCP server, so isolation is structural, not just a permission check. A compromised per-agent key cannot enumerate your other agents or touch contacts, billing, or integrations.