Quickstart
One REST API - and MCP server - for sending, inboxes and forwarding. Create an inbox, send an email and verify a domain in about a minute.
Authentication
Every request to /v1 is authenticated with a bearer key in the Authorization header. Live keys start with sk_live_; test-mode keys start with sk_test_. The base URL is https://www.threadcamp.com/v1.
To try the API with no signup, use the public sandbox key sk_test_sandbox_threadcamp_public_demo. Every response carries an x-request-id header for support.
Test-mode keys never deliver. A send from an sk_test_ key is validated in full — including that from is one of your inbox addresses — then returns 200 with "delivered": false and "mode": "test" instead of sending. It is not counted against your plan. Everything else on a test key is real: inboxes, domains and routes you create exist.
Authorization: Bearer sk_live_...
# base URL
https://www.threadcamp.com/v160-second quickstart
1. Create an inbox
curl https://www.threadcamp.com/v1/inboxes \
-H "Authorization: Bearer sk_test_sandbox_threadcamp_public_demo" \
-H "Content-Type: application/json" \
-d '{ "display_name": "Support Agent" }'
# -> 201
# { "id": "inbox_...", "object": "inbox",
# "address": "support-agent@relay.threadcamp.com",
# "autonomy": "auto", "webhook_secret": "..." }2. Send an email
curl https://www.threadcamp.com/v1/emails \
-H "Authorization: Bearer sk_test_sandbox_threadcamp_public_demo" \
-H "Content-Type: application/json" \
-d '{
"from": "support-agent@relay.threadcamp.com",
"to": ["you@example.com"],
"subject": "Hello from ThreadCamp",
"text": "Sent in one call."
}'
# -> 200 (sandbox key is test mode: validated, not delivered)
# { "id": "msg_test_...", "object": "message",
# "status": "test", "mode": "test", "delivered": false, ... }
#
# With a live key (sk_live_...) the same call returns:
# -> 201
# { "id": "msg_...", "object": "message", "status": "sent", ... }3. Add and verify a domain
# 1. Add the domain
curl https://www.threadcamp.com/v1/domains \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{ "name": "acme-agents.email" }'
# -> 201 with dns_records: THREE CNAME (dkim), TXT (spf),
# MX (receiving), TXT (dmarc). Place them at your DNS host.
#
# The MX goes on mail.acme-agents.email, not the root. MX is the one
# record that cannot be shared: ours at your root domain would take
# over every mailbox on it, so Google Workspace or Microsoft 365
# there would break. Your sending identity stays on the root, so you
# still send as you@acme-agents.email. Pass inbound_host to choose
# the root deliberately.
# 2. Run a verification check once the records are live.
curl -X POST https://www.threadcamp.com/v1/domains/dom_.../verify \
-H "Authorization: Bearer sk_live_..."
# -> Every record comes back annotated, so you can see which one is
# holding you up instead of guessing:
# {
# "status": "pending",
# "sending_ready": false,
# "dns_records": [
# { "purpose": "dkim", "status": "verified", "found": ["..."] },
# { "purpose": "dkim", "status": "missing", "found": [] },
# { "purpose": "spf", "status": "conflict",
# "found": ["v=spf1 include:_spf.google.com ~all"],
# "detail": "Add include:amazonses.com to the EXISTING record -
# a second v=spf1 record breaks SPF entirely." }
# ]
# }
#
# Or from a terminal: npx @threadcamp/cli dns check acme-agents.emailThe whole path, without a browser
Every step below is an HTTP call. There is no dashboard to click through, no CAPTCHA, no email confirmation and no card required, so an agent can go from nothing to a verified custom domain unattended. This is the sequence worth copying if you are automating a setup.
# 1. An account and a key. No email, no password, no dashboard.
curl https://www.threadcamp.com/v1/account -H "Content-Type: application/json" -d '{}'
# -> { "api_key": { "key": "sk_test_...", "mode": "test" }, "machine_account": true }
# The key is shown ONCE and cannot be recovered. Store it now.
# 2. An address you can send from immediately, on the shared relay.
curl https://www.threadcamp.com/v1/inboxes \
-H "Authorization: Bearer sk_test_..." \
-H "Content-Type: application/json" \
-d '{ "display_name": "Notifications", "autonomy": "auto" }'
# 3. Send. A sk_test_ key validates everything and delivers nothing,
# and hands back the rendered parts so you can check them.
curl https://www.threadcamp.com/v1/emails \
-H "Authorization: Bearer sk_test_..." \
-H "Content-Type: application/json" \
-d '{ "from": "notifications@relay.threadcamp.com",
"to": ["you@example.com"],
"subject": "Reset your password",
"markdown": "Tap the button below.",
"button": { "text": "Reset your password", "url": "https://acme.com/r/abc" } }'
# 4. Your own domain: add it, publish the records, verify (above).
# 5. A production inbox on it, adopted as the account contact in one call -
# which is how a machine account moves off its placeholder address.
curl https://www.threadcamp.com/v1/inboxes \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{ "username": "hello", "domain": "acme-agents.email",
"set_as_account_email": true,
"forward_to": "founder@acme.com" }'
# 6. Confirm before you rely on it: is this key live, is the domain ready?
curl https://www.threadcamp.com/v1/me -H "Authorization: Bearer sk_live_..."Prefer typed calls? npm i @threadcamp/sdk gives you the same surface with typed errors and an automatic idempotency key on every send, so a retry after a timeout cannot deliver twice. For a terminal, npx @threadcamp/cli whoami and npx @threadcamp/cli dns check yourdomain.com. For an agent, the MCP server.
Core concepts
Inbox
A real, addressable mailbox created via POST /v1/inboxes. Inboxes mint on the shared relay.threadcamp.com domain by default; verified custom domains host your own. Messages are listed per inbox and filterable by direction (inbound or outbound).
Autonomy policy
Per-inbox send behaviour: auto sends immediately, approve-first holds each send for human approval, and notify sends but labels the message for a human to see.
Route + SRS
A forwarding rule (POST /v1/routes): a catch-all (*@domain) or alias match forwarding to an off-platform address. SRS envelope rewriting and loop guards are applied automatically so SPF and DMARC survive the forward - nothing to configure.
Domain
A custom domain added via POST /v1/domains. The response includes the DNS records to place; POST /v1/domains/:id/verify runs a REAL verification check (DKIM polling) and the domain stays pending until your records propagate - then you can create inboxes and send from it.
Per-inbox webhooks
Webhooks are per-inbox: set callback_url on an inbox (POST /v1/inboxes or PATCH /v1/inboxes/:id) and inbound mail delivers an HMAC-signed email.received payload there. The signing webhook_secret is returned once at inbox creation.
Keep going
- API reference - every endpoint, request and response.
- MCP server - use ThreadCamp as agent tools.
- /openapi.json - the machine-readable spec.
- /llms.txt - the agent-facing site map.
Get a key and build.
Full API, MCP and webhooks on the free tier. No credit card.