Questions · Updated 2026-09-27
How do I find out why an email was not sent?
Find out why an email was not sent by reading the refusal POST /emails returned, or, once the email has an id, by calling GET /emails/{id}/explain, which returns verdict, what_happened, evidence, retryable and actions. GET /logs lists every request with its error_code, and POST /emails/preflight runs every gate without sending.
Find out why an email was not sent in one of two places, depending on whether it has an id. A refused POST /emails creates no message and returns no id: the answer is in the response body, in code, message, fix, docs_url and retryable. An accepted email has an id, and GET /emails/{id}/explain answers with verdict, what_happened, evidence, retryable and actions. GET /logs keeps every request the account made with its error_code, so a refusal you no longer have the response for is still on record.
A send that was refused
Read fix first; it names the call to make next. When retryable is false, waiting changes nothing, so stop and change what the fix says. When it is true, retry_after_seconds is present and the same request is sent again after that wait. All 87 codes are in the error catalogue. One code is not a refusal: approval_required means the send is held for a person, action_id names it, and GET /agent-actions lists it with its state.
GET /logs is the record. Its summary is "Every API request this account made: method, route, status, duration and the refusal code. Filter by date range, status, status class, method, route or key." GET /logs?error_code=domain_not_verified returns every request refused for that reason, and GET /logs?api_key_id={id} narrows to one key. Each row has method, path, status, error_code, request_id and created_at. GET /logs/{id} is one row; its summary is "One request, by log id. The x-request-id the caller saw is on the row."
An email that has an id
GET /emails/{id} returns status and last_event. GET /emails/{id}/explain turns that into a sentence and a plan. Its summary is "What happened to this email, the evidence, and the exact calls that fix it — machine-readable remediation." For a message that has not gone out, verdict is queued with "Accepted and about to send.", scheduled with "Waiting for its scheduled time.", or canceled with "Canceled before sending — nothing was delivered." A message that left and came back is bounce, complaint or delay, each with its own page. failure says "Our side failed to hand the message to the provider — nothing reached the recipient."
curl -sS -X GET https://api.agentisend.com/emails/9c8f8f0e-3d1a-4d3f-9a1e-2b7c1a0f5e42/explain \
-H "Authorization: Bearer $AGENTISEND_API_KEY"200
{
"actions": [],
"evidence": {},
"id": "9c8f8f0e-3d1a-4d3f-9a1e-2b7c1a0f5e42",
"retryable": true,
"status": "string",
"verdict": "string",
"what_happened": "string"
}Each entry in actions has a do sentence and, where a call can do it, a call with method, path and body. The MCP description of the same read adds: "A step with no call is one only a person can do." GET /emails/{id}/events is the raw timeline behind the verdict; its summary is "Every event recorded for one message, oldest first, with the provider detail."
Before the send
POST /emails/preflight answers the question in advance. Its summary is "Run EVERY send gate without sending: domain, sandbox, trust, suppression, budget, loop, content, verifier. The report matches what POST /emails would do, byte for byte — one code path." It returns ok, error and checks, with one entry per gate: validation, domain_scope, suppression, account, trust, domain, budget and loop among them. When ok is false, error is the refusal the send would have returned.
Over MCP
why_was_this_not_sent is the tool for this page's question. Its description says "Name an email_id for one message, or omit it to ask what is blocking this key and this account right now." It returns "every blocker, each with why, the fix, and is_normal_wait", and that last field separates a scheduled send from a paused key. explain_email is the twin of GET /emails/{id}/explain, and whoami returns can_send_now for the connection as a whole.