Docs

Integrating

MCP server

Every tool with the scopes it needs, the rate-limit table, the JSON-RPC error codes, and what an agent cannot do.

MCP server

Every tool this MCP server serves, the scopes each one needs, the ceilings it is counted against, and what each refusal means. Generated from the running catalogue — the page, the console tab and the server cannot disagree.

Server URL: https://api.agentisend.com/mcp (streamable HTTP). Bearer API key or OAuth 2.1 — both reach the same tools with the same ceilings. A local stdio launcher is published as @agentisend/mcp-server for clients that only speak stdio; it proxies to this server, so the catalogue is identical.

Add it to your client

Every line below carries the same endpoint (https://api.agentisend.com/mcp) and a placeholder key — replace as_YOUR_API_KEY_HERE with a key from the console.

Cursor

cursor://anysphere.cursor-deeplink/mcp/install?name=agentisend&config=eyJ1cmwiOiJodHRwczovL2FwaS5hZ2VudGlzZW5kLmNvbS9tY3AiLCJoZWFkZXJzIjp7IkF1dGhvcml6YXRpb24iOiJCZWFyZXIgYXNfWU9VUl9BUElfS0VZX0hFUkUifX0=

Open this link and Cursor offers to add the server. Replace the placeholder key in Settings → MCP afterwards, or paste the same object into .cursor/mcp.json.

Claude Code

claude mcp add --transport http agentisend https://api.agentisend.com/mcp \
  --header "Authorization: Bearer as_YOUR_API_KEY_HERE"

Or install the plugin, which brings the server and the sending skill together: /plugin marketplace add fortuneflick/agentisend-claude-plugin then /plugin install agentisend@agentisend.

Codex CLI

codex mcp add agentisend --url https://api.agentisend.com/mcp \
  --bearer-token-env-var AGENTISEND_API_KEY

The token stays in your environment; Codex writes only the variable name to ~/.codex/config.toml.

Gemini CLI

gemini mcp add --transport http \
  --header "Authorization: Bearer as_YOUR_API_KEY_HERE" \
  agentisend https://api.agentisend.com/mcp

Check it with gemini mcp list.

VS Code

code --add-mcp '{"name":"agentisend","type":"http","url":"https://api.agentisend.com/mcp","headers":{"Authorization":"Bearer as_YOUR_API_KEY_HERE"}}'

Or write the same server into .vscode/mcp.json under servers.

Windsurf

{
  "mcpServers": {
    "agentisend": {
      "serverUrl": "https://api.agentisend.com/mcp",
      "headers": {
        "Authorization": "Bearer as_YOUR_API_KEY_HERE"
      }
    }
  }
}

Goes in ~/.codeium/windsurf/mcp_config.json, then refresh the MCP panel.

OpenCode

{
  "mcp": {
    "agentisend": {
      "type": "remote",
      "url": "https://api.agentisend.com/mcp",
      "enabled": true,
      "headers": {
        "Authorization": "Bearer {env:AGENTISEND_API_KEY}"
      }
    }
  }
}

Goes in opencode.json; the key is read from your environment rather than written to the file.

Anything else that speaks stdio

AGENTISEND_API_KEY=as_YOUR_API_KEY_HERE npx -y @agentisend/mcp-server

The local launcher proxies to the same server, so the tool list, the budget and the refusals are identical.

Tools (34)

Every tool that changes something accepts idempotency_key: retrying with the same key returns the first result instead of doing the work twice.

Sending

ToolScopesIdempotentDestructive
send_emailemails:sendyes
send_batchemails:sendyes

Writes

ToolScopesIdempotentDestructive
create_domaindomains:writeyes
set_limitlimits:writeyesyes
request_approvalemails:sendyes
cancel_emailemails:sendyesyes
reschedule_emailemails:sendyes
verify_domaindomains:writeyes
add_suppressionsuppressions:writeyes
remove_suppressionsuppressions:writeyesyes
publish_templatetemplates:writeyes
upsert_contactcontacts:writeyes
delete_contactcontacts:writeyesyes
create_webhookwebhooks:writeyes
delete_webhookwebhooks:writeyesyes

Reads

ToolScopesIdempotentDestructive
get_emailemails:read
list_emailsemails:read
get_domaindomains:read
get_agent_budgetlimits:read
get_kill_switch_statelimits:read
list_pending_approvalslimits:read
list_agent_actionslimits:read
explain_emailemails:read
preflight_emailemails:send
whoami_none_
why_was_this_not_sentemails:read
get_trust_standingtrust:read
list_domainsdomains:read
list_suppressionssuppressions:read
list_templatestemplates:read
render_templatetemplates:read
list_segmentssegments:read
list_contactscontacts:read
list_webhookswebhooks:read

Scopes

A key holds scopes; a tool needs some. A tool you cannot call is missing one — whoami returns the scopes this credential holds and the tools they reach.

ScopeWhat it allowsTools
emails:readView messages you have sent, their delivery status and their events4
emails:sendSend 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 send6
domains:readView your sending domains, their region and the state of their DNS records2
domains:writeAdd sending domains and run domain verification2
webhooks:readView your webhook endpoints and their delivery history1
webhooks:writeCreate, update or remove webhook endpoints2
limits:readView the budget, rate ceiling and remaining allowance on each key, and the actions waiting for your decision4
limits:writeChange budgets and rate ceilings, and pause or resume a key — this changes how much can be spent without you1
trust:readView your account standing and the reasons behind it1
contacts:readView the people in your contact list and what you have recorded about them1
contacts:writeAdd people to your contact list, change what is recorded about them, and remove them2
suppressions:readView the addresses you no longer send to, and why each one is on the list1
suppressions:writeAdd addresses you no longer want contacted, and take one off the list — except where somebody unsubscribed or reported a message as spam, which only you can undo2
templates:readView your templates and preview one with sample values2
templates:writePublish a template, which decides what the next send using it will say1
segments:readView the groups you have defined and who is currently in them1

Rate limits

Per-minute ceilings are the same on every plan and are counted per API key, by operation class. What a plan changes is how much you may send in total.

Operation classCalls per minute per keyTools
read12019
write6013
send302
PlanEmails included per monthDaily send cap
Starter30,000none
Pro100,000none
Scale100,000none

JSON-RPC error codes

Every refusal arrives as a tool result carrying code, message, fix, reason, retryable and jsonrpc_code. Branch on jsonrpc_code or reason, never on the message.

CodeNameWhenWhat to doWait hint
-32001insufficient_scopeThe key is valid but was not granted a scope this tool requires.Read missing_scopes in the error data — it names exactly which ones. Ask the person to create a key with those scopes and reconnect. Retrying with the same key fails identically.none
-32002rate_limitedThe per-minute ceiling for this operation class was reached.Wait retry_after_seconds and repeat the same call. This is the ONLY refusal class where waiting is the fix.retry_after_seconds
-32005limit_reachedA configured ceiling was hit — the send budget for the period, or a plan cap.Stop. Waiting does not cure it: a person raises the number or the period rolls over. get_agent_budget says when the period resets.none
-32006not_foundNo such record on this account.Check the id. Do not retry — the same id will be absent next time too.none
-32008conflictThe record already exists, or changed under you.Read the current state and decide again. A blind retry repeats the conflict.none
-32009failed_preconditionThe request is well-formed but something about the account state refuses it — an unverified domain, a suppressed recipient, a paused key, a held send.Read fix: it names the endpoint or the person. This class covers every deliberate stop, and none of them is cured by retrying.none
-32010temporarily_unavailableA dependency was briefly unavailable.Retry once after retry_after_seconds. If it repeats, report it and stop.retry_after_seconds
-32602invalid_paramsThe arguments did not validate. Nothing ran.The issues list names every field to correct. Retrying unchanged fails the same way.none
-32603internal_errorSomething failed that should not have.Retry ONCE after a short pause with the same idempotency_key, then stop and report the request_id from the error. It identifies this exact failure in support.none

The refusals you will actually meet

Error codeJSON-RPCRetryableFix
kill_switch_active-32001no — waiting cannot helpRead GET /trust/standing for why it was paused. Only a person signed in to the console can resume it; the paused key cannot resume itself.
agent_budget_exceeded-32005no — waiting cannot helpWait for the period to reset — get_agent_budget and GET /limits/keys/:id both say when. Raising a budget is a person’s decision, made in the console; a key cannot raise its own.
approval_required-32001no — waiting cannot helpIt is waiting in the console approvals inbox; GET /agent-actions shows it and what it says. A person decides — the key that asked cannot approve itself.
trust_paused-32001no — waiting cannot helpReview reasons via GET /trust/standing, then file an appeal via POST /trust/appeal.
rate_ceiling_exceeded-32002after 60sWait the seconds below and send the same request again. Raising the ceiling is a person’s decision, made in the console; a key cannot raise its own.
rate_limiter_unavailable-32010after 5sRetry in a few seconds. Nothing was sent and nothing was changed — writes are refused rather than run unmetered against a shared sending reputation.
restricted_api_key-32001no — waiting cannot helpUse a full_access key (POST /api-keys with permission=full_access) for management endpoints.
suppressed_recipient-32009no — waiting cannot helpGET /suppressions says which address and why. A hard bounce you have fixed can be cleared with DELETE /suppressions/:id; an unsubscribe or a spam complaint cannot — that address asked not to be contacted.
domain_not_verified-32001no — waiting cannot helpAdd the DNS records shown by GET /domains/:id, then call POST /domains/:id/verify.

What an agent cannot do here

These are deliberate stops with a person on the other end, not gaps.

  • Lift your own kill switch. A rest an agent can cut short is not a rest. _A person, on the Agents screen in the console. You may stop yourself at any time — POST /limits/keys/:id/kill and /limits/kill-all stay open to a key._
  • Approve your own held send. Approval you can grant yourself is not approval. _A person, in the approvals inbox. Use request_approval to ask; list_agent_actions to hear the answer._
  • Raise your own budget or your own per-minute ceiling. A budget an agent can raise is not a budget. _A person, on the Agents screen in the console. set_limit and PATCH /limits/keys/:id still lower a key, so an agent can always cap itself before a job._
  • Raise the account trust standing, or waive a trust pause. Trust is earned by delivery outcomes, not asserted. _Nobody directly — it changes as your sending does. POST /trust/appeal asks a human to look._
  • Read the contents of a message you were stopped from sending. A held-mail queue an agent can read verbatim is a way to read the mail it was stopped from sending. _A person, in the console. Tools see shapes: recipient counts, whether there is a body._
  • Send to an address that asked not to be contacted. Suppression is the whole reason the account is allowed to send at all. _Nobody, while the suppression stands. remove_suppression clears a hard bounce you have fixed; an unsubscribe or a spam complaint is refused, because somebody said no and only they can change that._
  • Send unsolicited mail to people who did not ask for it. This product refuses cold outreach by design. There is no feature for it and asking for one is out of scope. _Nobody, at any tier, by any route. There is no endpoint for it and no plan that adds one._