Agents · Published 2026-09-04 · Updated 2026-10-27 · AgentiSend
Agent quickstart
Six numbered steps from no account to a delivered email, with the exact request and the exact response shape for each.
Read this if you are a model or a script. Every step is one request. Every request is printed with the response you should expect, and every response shape below comes from openapi.json rather than from prose — the same document at /openapi.json.
Before step 1: $AGENTISEND_API_KEY holds a key that starts with as_. A human creates the first one in the console; step 3 creates the rest. If you do not have one, stop and ask for one. There is no anonymous send.
The base URL is https://api.agentisend.com. Every response carries ratelimit-limit, ratelimit-remaining and ratelimit-reset. Read them; do not discover the ceiling by hitting it.
1. Add the domain you will send from
curl -sS -X POST https://api.agentisend.com/domains \
-H "Authorization: Bearer $AGENTISEND_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "yourdomain.com"}'Region is optional and defaults to us. "region": "eu" is recorded on the domain; today all mail is sent from our US nodes, and eu domains move to EU sending automatically when it ships.
2. Read the DNS records back, publish them, then verify
GET /domains/{id} returns the records to publish. A human (or your DNS API) publishes them; you then ask for a re-check:
curl -sS -X POST https://api.agentisend.com/domains/9c8f8f0e-3d1a-4d3f-9a1e-2b7c1a0f5e42/verify \
-H "Authorization: Bearer $AGENTISEND_API_KEY" \
-H "Idempotency-Key: $(uuidgen)"200
{
"capabilities": {
"receiving": "enabled",
"sending": "enabled"
},
"click_tracking": false,
"created_at": "2026-09-04T09:14:00.000Z",
"dkim_selector": "example",
"id": "9c8f8f0e-3d1a-4d3f-9a1e-2b7c1a0f5e42",
"inbound_hold_mode": "monitor",
"inbound_policy": "open",
"inbound_refused_count": -9007199254740991,
"name": "example",
"object": "domain",
"open_tracking": false,
"provider_hints": {
"cloudflare": "example",
"godaddy": "example",
"namecheap": "example",
"route53": "example"
},
"recent_events": [],
"records": [],
"region": "us",
"return_path_subdomain": "example",
"status": "pending",
"tls_mode": "opportunistic",
"tracking_issues": [],
"tracking_subdomain": "example",
"verified_at": "2026-09-04T09:14:00.000Z",
"warnings": [],
"zone": "example"
}Until this returns a verified status, a send from that domain answers domain_not_verified, and that error names this endpoint as the fix. Do not retry the send in a loop — retry the verification, and only after the records are actually published.
3. Create your own key, with its own budget
Do not send with the key a human uses. A shared key is a shared blast radius and one ambiguous line in the audit log.
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":"example","permission":"full_access"}'201
{
"budget_per_period": 5000,
"created_at": "2026-09-04T09:14:00.000Z",
"domain_scope": "example",
"expires_at": "2026-09-04T09:14:00.000Z",
"id": "9c8f8f0e-3d1a-4d3f-9a1e-2b7c1a0f5e42",
"last_used_at": "2026-09-04T09:14:00.000Z",
"loop_guard": "off",
"name": "example",
"object": "api_key",
"period": "monthly",
"permission": "full_access",
"previous_key_expires_at": "2026-09-04T09:14:00.000Z",
"rate_ceiling_per_minute": 600,
"request_count_30d": -9007199254740991,
"rotated_at": "2026-09-04T09:14:00.000Z",
"scopes": [],
"system": false,
"token": "as_abababababababababababababababababababababababababababababababab",
"token_prefix": "as_abababab"
}The token is returned exactly once. Store it before you make another request; there is no endpoint that will show it to you again. The key is created with a budget already on it — 252 operations exist and none of them creates an unlimited sender.
4. Send
curl -sS -X POST https://api.agentisend.com/emails \
-H "Authorization: Bearer $AGENTISEND_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"from":"billing@yourdomain.com","to":"delivered@simulator.agentisend.com","subject":"Your receipt","text":"Thanks — the details are attached."}'201
{
"id": "9c8f8f0e-3d1a-4d3f-9a1e-2b7c1a0f5e42",
"simulated": true,
"warnings": []
}Send Idempotency-Key on every send. If your process dies between the request and the response, repeating the call with the same key replays the original result instead of sending twice. Keys are remembered for 7 days.
For more than one message, use POST /emails/batch: each item succeeds or fails on its own, so one malformed address does not lose the other 499.
5. Read what happened
curl -sS -X GET https://api.agentisend.com/emails/9c8f8f0e-3d1a-4d3f-9a1e-2b7c1a0f5e42 \
-H "Authorization: Bearer $AGENTISEND_API_KEY"200
{
"api_key_id": "example",
"attachments": [],
"bcc": [],
"cc": [],
"created_at": "2026-09-04T09:14:00.000Z",
"domain": "example",
"from": "example",
"headers": {},
"html": "example",
"id": "9c8f8f0e-3d1a-4d3f-9a1e-2b7c1a0f5e42",
"last_event": "queued",
"message_id": "example",
"object": "email",
"provider_message_id": "example",
"reply_to": [],
"scheduled_at": "2026-09-04T09:14:00.000Z",
"status": "queued",
"subject": "example",
"tags": {},
"template_id": "9c8f8f0e-3d1a-4d3f-9a1e-2b7c1a0f5e42",
"template_version": -9007199254740991,
"text": "example",
"to": [],
"updated_at": "2026-09-04T09:14:00.000Z",
"warnings": []
}The status is one of 51 event types' worth of history, and GET /emails/{id}/mime returns the exact bytes that left, which is the thing to attach when a recipient says an email never arrived.
For a running process, subscribe a webhook rather than polling: POST /webhooks with the events you care about. A failed delivery is retried up to 10 times in all — after 5 seconds, 5 minutes, 30 minutes, 2 hours, 5 hours, 10 hours, 10 hours, 24 hours, 48 hours — and then dead-lettered where you can replay it.
6. Check your standing before a large send
curl -sS -X GET https://api.agentisend.com/trust/standing \
-H "Authorization: Bearer $AGENTISEND_API_KEY"200
{
"domains": [],
"history": [],
"next_review_at": "2026-09-04T09:14:00.000Z",
"probation": false,
"probation_cap": -9007199254740991,
"probation_ends_at": "2026-09-04T09:14:00.000Z",
"reason_codes": [],
"reasons": [],
"sla_deadline_at": "2026-09-04T09:14:00.000Z",
"state": "ok",
"streams": {
"marketing": {
"bounce_rate": 1,
"bounced": -9007199254740991,
"complained": -9007199254740991,
"complaint_rate": 1,
"sent": -9007199254740991,
"state": "ok"
},
"transactional": {
"bounce_rate": 1,
"bounced": -9007199254740991,
"complained": -9007199254740991,
"complaint_rate": 1,
"sent": -9007199254740991,
"state": "ok"
}
},
"window_days": -9007199254740991
}If this is not ok, a bulk send is the wrong move: the ladder acts on bounce rate at 2% (warn) and 4% (pause), and on complaint rate at 0.05% (warn) and 0.08% (pause). Complaint counts come from mailbox providers that send us feedback reports; Gmail, Yahoo and Outlook do not send them to us today, so a complaint rate of 0 is not evidence that nobody complained. Fix the list first.
The errors you will actually meet
Every 4xx carries code, message, fix and docs_url. Act on fix; it names the endpoint to call next. The five that matter to an autonomous sender:
| Code | What happened | What to do |
|---|---|---|
missing_api_key | No bearer token on the request. | Send Authorization: Bearer as_…. Do not retry without one. |
domain_not_verified | The from-domain has not passed DNS checks. | Publish the records, call POST /domains/{id}/verify, then send. |
agent_budget_exceeded | This key has spent its send budget for the period. | Stop. Ask a human to raise the budget with PATCH /limits/keys/{id}, or wait for the period to reset. Retrying does not help. |
plan_limit_reached | The account has used every email included in its plan this period. | Upgrade in Settings → Billing, or wait until the reset date in the error. |
rate_limit_exceeded | Too many requests in the window. | Wait retry_after_seconds, then continue. This one is safe to retry. |
approval_required | The loop guard flagged the send, or it was sent through request_approval, and it is held. Approval rules you define yourself are not available yet. | Poll the action until a human approves or rejects it. Do not resubmit — a second submission is a second held send. |
kill_switch_active | A human paused this key, or every key on the account. | Do not work around it; report it. Resume is POST /limits/resume-all or POST /limits/keys/{id}/resume. |
The full catalogue, all 122 of them, is at /docs/errors and as Markdown at /docs/errors.md.
The two limits you cannot raise from inside a session
The budget. When it is spent, sends stop and agent_budget_exceeded (the key) or plan_limit_reached (the account inclusion) says so. Nothing is auto-charged, nothing is quietly allowed through, and no request you can make lifts your own ceiling. That is the point of it.
The kill switch. A human can stop every key on the account with POST /limits/kill-all. If your sends start answering kill_switch_active, a person did that deliberately. Do not work around it; report it.
7. Receive at your domain, if the agent needs replies
Turn receiving on for a verified domain (PATCH /domains/{id} with capabilities.receiving.enabled), publish the one MX record its page then shows, and each message to that domain arrives as email.received. GET /emails/receiving/{id} returns extracted_text, the reply without quoted history, beside the raw body, and inbound_verdict says clear or review. A domain can hold review mail until a person releases it, and can refuse senders with block rules or admit only an allow list. Receiving and replying has every call.
What this service will not do
No cold outreach. There is no lead database, no scraping, and no bulk import of addresses that never asked to hear from you. A request that looks like unsolicited sending is refused, and asking differently will not change the answer.
Where everything else is
The spending limit on a key, the way to stop a loop that is already sending, and the one-sentence definition of this API are Email API with spending limits for AI agents, Stop an AI agent sending emails in a loop and Transactional email API for AI agents.
- /llms.txt — the index: figures, plans, every operation, error and event.
- /llms-full.txt — every page on this site as Markdown.
- /.well-known/agent.json — every URL above, in one document.
- /how-agents-see-this-site — the fetching contract, with a curl for each claim.
- /docs/guides/agents — the same API over MCP, if you are a client rather than a script.