The whole email stack for agents, one API.
ThreadCamp reconciles inboxes, sending and forwarding behind one key. Every primitive below maps to a real endpoint under /v1 - no glue code between three vendors.
Inboxes as a primitive
POST /v1/inboxes creates a real, addressable inbox on the shared relay.threadcamp.com domain; pass a verified custom domain for your own. Each inbox carries an autonomy policy and messages you can list by direction.
Resend-compatible sending
POST /v1/emails accepts the Resend payload shape - from, to, subject, text, html, cc - plus scheduled_at for delayed send and client_id for idempotency. Migrate by changing the base URL and key.
Forwarding Routes with SRS
POST /v1/routes defines a catch-all (*@domain) or alias Route that forwards to an off-platform address. SRS envelope rewriting and loop guards are applied automatically so SPF and DMARC survive the hop.
Custom domains, real verification
POST /v1/domains returns the DNS record set to place at your host. POST /v1/domains/:id/verify runs a real DKIM verification check - the domain stays pending until your records propagate, then flips to verified.
Human-in-the-loop approvals
The differentiator. Set an inbox to approve-first and any send from it is held as status pending_approval with an approval_id. A human resolves it via POST /v1/approvals/:id - approving is what executes the send.
Signed webhooks + event log
Webhooks are per-inbox: set callback_url on an inbox and inbound mail delivers an HMAC-signed email.received payload there (the signing secret is returned once at inbox creation). GET /v1/events is the inspectable activity log, derived from your message records.
Human-in-the-loop approvals
The category's biggest objection is blast radius: what if the agent emails the wrong person, at scale? Set an inbox to approve-first and every send from it lands as a pending draft. The send does not go out until a human calls POST /v1/approvals/:id with action "approve" - and that approval is what actually sends it. Reject instead, and the message is marked rejected and never leaves.
How approvals workBuilt to be discovered and used unattended.
MCP server
A hosted and stdio (npx -y @threadcamp/mcp) MCP server exposes the API as callable tools - create_inbox, send_email, wait_for_message and more. See the MCP docs.
Machine-readable everything
llms.txt, /openapi.json and /pricing.json let an agent read the surface and self-select a plan.
Sandbox keys + idempotency
A public sandbox key (sk_test_sandbox_threadcamp_public_demo) lets agents try the API with no signup. client_id makes creates and sends safe to retry.
Create an inbox, send, forward.
Create an inbox (approve-first)
curl https://www.threadcamp.com/v1/inboxes \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{ "display_name": "Support Agent", "autonomy": "approve-first" }'
# -> 201
# { "id": "inbox_...", "object": "inbox",
# "address": "support-agent@relay.threadcamp.com",
# "autonomy": "approve-first", "created_at": "..." }Send an email (scheduled, idempotent)
curl https://www.threadcamp.com/v1/emails \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{
"from": "support-agent@relay.threadcamp.com",
"to": ["customer@example.com"],
"subject": "Your receipt",
"text": "Thanks for your order.",
"scheduled_at": "2026-08-01T09:00:00Z",
"client_id": "receipt-8891"
}'
# from an approve-first inbox -> 202
# { "id": "msg_...", "status": "pending_approval", "approval_id": "apr_..." }Create a forwarding Route
curl https://www.threadcamp.com/v1/routes \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{
"match": "*@acme-agents.email",
"action": "forward",
"destination": "team@acme.com",
"srs": true
}'
# -> 201
# { "id": "route_...", "object": "route", "match": "*@acme-agents.email",
# "action": "forward", "destination": "team@acme.com", "srs": true }Give your agent the full stack in one call.
Inboxes, sending and forwarding on the free tier. No credit card.