All Your InboxesStart free

API, webhooks & MCP

The same product, reached from code. Send from any address you connected, get every received email pushed to you, answer it in-thread from the business address, and give an AI agent a safe, scoped way to do the same.

1 · Authentication & keys

Create a key in the panel under Developers (or via POST /v1/api-keys with a manage key). Keys belong to one organization and carry scopes: send, read, reply, manage. Send it as a bearer token:

Authorization: Bearer ayi_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Base URL: https://allyourinboxes.com/api/v1. Every error is a JSON body { statusCode, name, message } with Resend's error names (validation_error, missing_api_key, rate_limit_exceeded, monthly_quota_exceeded…), so existing error handling keeps working.

2 · Send an email (Resend-compatible)

Same request body as Resend's POST /emails. The only rule: from must be an inbox you connected (or any address on a catch-all domain) with sending enabled.

curl https://allyourinboxes.com/api/v1/emails \
  -H "Authorization: Bearer ayi_live_…" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-1234" \
  -d '{
    "from": "Acme Support <support@acme.com>",
    "to": ["customer@example.com"],
    "reply_to": "support@acme.com",
    "subject": "Your order shipped",
    "html": "<p>On its way.</p>",
    "attachments": [{ "filename": "invoice.pdf", "content": "<base64>" }]
  }'
# → { "id": "…" }

Using the Resend Node SDK? Point it at us and keep the code:

import { Resend } from "resend";
const resend = new Resend("ayi_live_…", { baseUrl: "https://allyourinboxes.com/api/v1" }); // or RESEND_BASE_URL=https://allyourinboxes.com/api/v1
await resend.emails.send({ from: "support@acme.com", to: "customer@example.com", subject: "Hi", text: "Hello" });

Also available: POST /emails/batch (up to 100, index-aligned response), GET /emails/:id (status), GET /emails (recent sends). Threading: add "in_reply_to": "<received email id>" and we set In-Reply-To/References for you.

3 · Receive: webhooks

Add a URL under Developers (or POST /v1/webhooks). Choose events: email.received, email.blocked, reply.sent, email.sent, email.failed. Optionally limit to specific inboxes. Unlike metadata-only providers, email.received carries the text body and signed attachment links, so one request is usually all you need:

POST https://your-app.example/webhooks/mail
webhook-id: dlv_…            webhook-timestamp: 1725700000
webhook-signature: v1,MEUCIQ…   content-type: application/json

{
  "type": "email.received",
  "created_at": "2026-09-07T10:12:00.000Z",
  "data": {
    "email_id": "cm…", "inbox_id": "cm…", "inbox": "support@acme.com", "project": "Acme",
    "from": "Jane <jane@example.com>", "from_address": "jane@example.com", "to": "support@acme.com",
    "subject": "Refund?", "message_id": "<…@example.com>", "received_at": "…",
    "authenticated": true,
    "spam_score": 0.03, "category": "customer",
    "text": "Hi, I'd like a refund for…",
    "text_expires_at": "2026-09-08T10:12:00.000Z",
    "attachments": [{ "id": "…", "filename": "receipt.pdf", "content_type": "application/pdf", "size": 48213,
                      "download_url": "https://allyourinboxes.com/api/v1/attachments/…?exp=…&sig=…", "expires_at": "…" }]
  }
}

Delivery: 10-second timeout, any 2xx counts as received. Failures retry after 1 min, 5 min, 30 min, 2 h and 12 h. After 20 consecutive failures the endpoint is paused and shown as such in the panel; press Resume to re-enable. Send test fires a webhook.test event immediately.

4 · Verify a webhook signature

We follow the Standard Webhooks spec, so any library for it works. By hand:

import { createHmac, timingSafeEqual } from "node:crypto";

export function verify(secret, headers, rawBody) {
  const id = headers["webhook-id"], ts = headers["webhook-timestamp"], sig = headers["webhook-signature"];
  if (Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false;          // 5-minute replay window
  const key = Buffer.from(secret.replace(/^whsec_/, ""), "base64");
  const expected = createHmac("sha256", key).update(`${id}.${ts}.${rawBody}`).digest("base64");
  return sig.split(" ").some((s) => {
    const v = s.replace(/^v1,/, "");
    return v.length === expected.length && timingSafeEqual(Buffer.from(v), Buffer.from(expected));
  });
}

Use the raw request body (before JSON parsing). webhook-id is unique per delivery — use it to de-duplicate retries.

5 · Read received mail & attachments

GET https://allyourinboxes.com/api/v1/emails/receiving?limit=20&unanswered=true&domain=acme.com&q=refund
GET https://allyourinboxes.com/api/v1/emails/receiving/:id                # text + attachment links while retained
GET https://allyourinboxes.com/api/v1/emails/receiving/:id/attachments
GET https://allyourinboxes.com/api/v1/inboxes                             # connected addresses (alias: /domains)
GET https://allyourinboxes.com/api/v1/events?kind=blocked                 # the audit ledger

Retention: by design we delete message bodies once the notification is delivered. When your organization has a webhook or a read key, bodies and attachments are kept for 24 hours after arrival and then removed; text_expires_at tells you when. Metadata (from, subject, dates, answered) stays.

6 · Reply in-thread

The one thing a sending API can't do for you: answer a customer from the address they wrote to, inside the same conversation. We set In-Reply-To/References and send through that inbox's own sending path.

curl https://allyourinboxes.com/api/v1/emails/receiving/cm…/reply \
  -H "Authorization: Bearer ayi_live_…" -H "Content-Type: application/json" \
  -d '{ "text": "Refund issued — you will see it in 3–5 days." }'
# → { "id": "…" }   (reply.sent webhook follows)

Block a sender: POST /inboxes/:id/blocked with { "pattern": "@spammer.com" }.

7 · MCP server for AI agents

Give Claude, Cursor, VS Code or any MCP client a key with the scopes you want the agent to have. Endpoint: https://allyourinboxes.com/mcp (Streamable HTTP, stateless).

{
  "mcpServers": {
    "allyourinboxes": {
      "url": "https://allyourinboxes.com/mcp",
      "headers": { "Authorization": "Bearer ayi_live_…" }
    }
  }
}

Tools: list_inboxes, list_emails (filters: inbox, domain, from, subject, unanswered, needs_reply, urgent), get_email (text + attachment links), reply_to_email (in-thread, html and attachments allowed), send_email, block_sender, list_events. A read-only key makes the agent read-only — scopes are enforced per tool, not by the agent's good behaviour.

8 · Give an agent its own address

Create a key scoped to one inbox in the panel (Developers → API keys → pick an inbox instead of “account-wide”). That key can only ever read, send and reply as that one address — not another inbox on the same account, not even a catch-all on the same domain. It can't create or revoke other keys, manage webhooks, or see other inboxes; those always need an account-wide key. One key per agent means one agent going rogue, or one key leaking, costs you exactly one inbox.

Every received email also carries Jev triage — spam_score, category, needs_reply, urgency — so an agent (or you) can ask list_emails for needs_reply: true and skip the rest.

9 · Agent safety: Jev vs. mail-borne manipulation

An agent that reads mail and acts on it is a new target for business-email-compromise: a message asking to change payment or bank details, redirect a shipment or a transfer, or quietly override earlier instructions — aimed at the automated reader, not a person. Every received email is also scored for exactly this by Jev, as agent_risk (0–1, same request as the spam screen).

At or above 0.6, reply_to_email and send_email (when threaded onto that message via in_reply_to) are refused with 422 agent_risk_blocked — whether called over MCP or REST. Nothing is hidden: the message still reaches the inbox and its notification, with a banner saying so, so a human can read it and reply normally by email or in the panel. The block exists for exactly one case — the no-human-in-the-loop path.

Filter for it directly: list_emails takes agent_risk: true, and plain-sentence rules (forward/flag/hold) never fire on a flagged message either, the same way they never fire on spam.

10 · Limits, errors, retention

  • Rate: 10 requests/second per key (600/min). Over it: 429 rate_limit_exceeded.
  • Monthly API sends: Starter 2,000 · Pro 10,000 · Business 50,000. Free: read-only API, 1 webhook, MCP. Over quota: 429 monthly_quota_exceeded.
  • Attachments: 10 MB per email, in and out.
  • Idempotency-Key: ≤256 chars, remembered 24 h; the same key with a different payload returns 409 invalid_idempotent_request.
  • Mailbox-scoped key outside its inbox: 403 mailbox_scoped_key (filters/sends) or 404 not_found (a specific email id in another inbox — the key is never told it exists).
  • Agent-risk mail: 422 agent_risk_blocked on a programmatic reply/send referencing it — see §9.
  • Where mail actually goes out from: your inbox's own SMTP or Google/Microsoft OAuth — we never send as you from our servers, which is why deliverability follows your domain's SPF/DKIM, not ours. Deliverability guide →

All your email. One place.

Connect any inbox. Send from the right address automatically. Free plan forever, paid from $4.99/mo, and it never touches how your mailboxes are set up.

Try it free