Questions · Updated 2026-09-28

Which API key permission should an AI agent have?

An AI agent should have its own key with permission sending_access, created with POST /api-keys and given a ceiling with PATCH /limits/keys/{id}. That key can send and read what it sent; management routes refuse it with restricted_api_key, and the routes only a person may call refuse every key with human_action_required.

An AI agent should have a key of its own with permission sending_access. Create it with POST /api-keys, whose body takes name, permission (full_access or sending_access), an optional scopes list and an optional domain_scope; the operation summary says "The token is shown exactly once." Then set the key's ceiling with PATCH /limits/keys/{id} from your own credentials. A sending_access key can call POST /emails and read the messages it sent. A management route answers it with restricted_api_key, so the agent cannot mint a key, move a limit or read another key's traffic.

The two permissions

full_access holds every scope. sending_access holds two: emails:send and emails:read. The MCP guide describes emails:send as "Send email from your verified domains — this delivers real mail to real people and spends the budget on this connection. It also covers asking you to approve a held send", and emails:read as "View messages you have sent, their delivery status and their events". A sending_access key reads only messages it sent itself; GET /emails from that key never returns another key's mail.

scopes narrows a key below its permission and cannot widen it. Asking for limits:write on a sending_access key is refused with invalid_parameter and the sentence "Those scopes are wider than this permission allows." Omit scopes to take the permission's default.

curl -sS -X POST https://api.agentisend.com/api-keys \
  -H "Authorization: Bearer $AGENTISEND_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"name":"yourdomain.com","permission":"sending_access"}'
201
{
  "budget_per_period": 1,
  "created_at": "2026-09-04T09:14:00Z",
  "domain_scope": "string",
  "expires_at": "2026-09-04T09:14:00Z",
  "id": "9c8f8f0e-3d1a-4d3f-9a1e-2b7c1a0f5e42",
  "last_used_at": "2026-09-04T09:14:00Z",
  "name": "yourdomain.com",
  "period": "hourly"
}
Response

What restricted_api_key covers

GET /api-keys, PATCH /limits/keys/{id}, GET /limits/keys/{id}, GET /logs and GET /usage are management routes. A sending_access key calling any of them gets restricted_api_key. The catalogue message is "This API key is restricted to sending only." and the fix is "Use a full_access key (POST /api-keys with permission=full_access) for management endpoints." retryable is false, so the agent stops rather than retrying with the same key.

The agent still has a read on its own ceiling: over MCP, whoami needs no scope and returns, in the words of its description, "budget consumed and remaining with its refill time", the scopes held and the tools they reach.

The person's view is GET /api-keys, which lists every key with permission, scopes, domain_scope, budget_per_period, last_used_at and request_count_30d. Its summary says permission, domain scope and the key's own budget ceiling are always returned, and the token never is. One agent per key keeps that list legible; two agents on one key are one row.

The ceiling and who moves it

Set budget_per_period, period and rate_ceiling_per_minute with PATCH /limits/keys/{id}. Its summary is "Set or clear this key’s period budget and per-minute ceiling. An API key may lower its own; raising one is a person’s decision, made in the console." A full_access key that tries to raise one is refused with human_action_required, and the message names the field: "Raising budget_per_period is a person’s decision, so an API key cannot make it. A key may lower its own limits at any time." Over MCP the same call is set_limit, titled "Lower key limits". When the budget is spent the send answers agent_budget_exceeded; that refusal is the subject of Agent email budgets.

What no key can do

Some routes are a signed-in person's whatever the key's permission: POST /agent-actions/{id}/approve, POST /agent-actions/{id}/reject, POST /limits/keys/{id}/resume and POST /limits/resume-all. A full_access key is refused with human_action_required, and a sending_access key with restricted_api_key. The catalogue message is "This is a person’s decision, so an API key cannot make it." and the fix is "Ask whoever runs this account to do it in the console. Scoping the key differently does not change the answer, and retrying fails the same way." So giving the agent full_access buys it the ability to create keys and edit limits, and nothing on those routes.

Next