Docs
Quickstart, MCP setup, CLI usage, and the full tool reference — written for both a human setting this up and an agent operating it.
Quickstart
- Sign up (no card required for the demo tier):
curl -s -X POST https://api.coldrig.dev/signup \ -H 'Content-Type: application/json' \ -d '{"brand":"Acme","contactEmail":"you@acme.com"}' # → { "tenantId": "ten_...", "token": "..." } - Hand the token to your agent — via MCP config, CLI env var, or directly as a bearer token if your agent talks HTTP.
- Have your agent call
setup_infrastructure, thenlaunch_campaign;infrastructure_statusshows warmup progress and is not a send gate. On a trial account, a campaign you launch plays out by itself as a simulation, with no demo step and whatever its sending hours, and no email reaches a real inbox. See the free demo.
MCP setup
For Claude Code, Codex, or any MCP-capable agent, add a remote MCP server pointing at the hosted endpoint:
{
"mcpServers": {
"agent-cold-email": {
"url": "https://api.coldrig.dev/mcp",
"headers": {
"Authorization": "Bearer <your-token>"
}
}
}
}
The hosted MCP server is streamable-HTTP and per-token scoped — one config line with your API key (apps that support OAuth sign-in, such as ChatGPT's custom connector, can use that instead). It is live in production; demo accounts run against simulated suppliers, and activated accounts reach real ones — see the status banner above.
CLI usage
npx agent-cold-email demo # run the full pipeline in test mode, no signup
npx agent-cold-email signup --brand Acme --email you@acme.com
npx agent-cold-email setup --brand Acme --primary-domain acme.com --domains 2 --inboxes-each 3
npx agent-cold-email status
npx agent-cold-email campaign launch --file campaign.json
The CLI is published on npm (v0.2.2) and is a thin twin over the same HTTP facade documented below. Every subcommand maps to a tool in the tool reference; the package remains the permanent machine handle for Coldrig.
The free demo
POST /signup mints a demo-plan account that is structurally incapable of reaching a real vendor adapter — enforced at the type level and covered by a test that fails if the guard is ever bypassed, not just documented policy. Every domain purchase, mailbox setup step, warmup tick, and send in demo mode runs against test mode that simulates bounces, complaints, and replies. On a trial account, a campaign you launch plays out by itself as a simulation, with no demo step and whatever its sending hours, and no email reaches a real inbox. Its sends, replies, bounces and follow-ups show in campaign_results, metrics and inbox. A short list is fully played out when the launch answers; a longer list answers with part of it played and finishes by itself, with no further call, and reading the results again shows the counts grow. Contacts you add and a campaign you resume play out the same way. A campaign launched before setup_infrastructure waits and starts by itself once setup has made the mailboxes. The launch, add-contacts and resume answers of a trial account carry simulation (sent, replies, bounces, complaints, waiting and, when waiting is above zero, reason), counting what that call played; a paid account's answers have no simulation. Among the contacts whose address has no marker word, the 2nd replies and, from three contacts up, the last one bounces; an address whose part before the @ contains complaint (a complaint), softbounce (a soft bounce), bounce (a hard bounce) or reply (a reply) gets that outcome instead. Nothing simulated pauses, throttles or replaces anything, on the trial or after you pay, and going live removes every bounce and complaint the simulation put on your suppression list, so those contacts can be mailed; an unsubscribe and an entry you added by hand stay. A trial account can launch up to 5 test campaigns in any 24 hours, each holding up to 100 contacts, which is enough for a small test. Both are limits of the trial, not of Coldrig: going live removes them. Nothing in a campaign launched on the trial is ever really sent: when the account goes live, the emails it still has waiting are canceled and it cannot be resumed or added to, so launch a new campaign to send real email. To see the built-in sample campaign instead, call POST /demo/run (HTTP only, not an MCP tool; bearer token; send no body). A run moves the clock forward 32 days, past the multi-week warmup ramp, launches its own built-in sample campaign, sends two steps, reads the test-mode inbox, and returns { sent, replies, bounces, complaints, stopOnReplyProof, sampleThread }; the built-in campaign stays listed as active. Call it after setup_infrastructure; it is limited to demo accounts (403 otherwise), one run a minute and 20 in total per account (429), and its built-in campaign cannot be resumed or added to. Full detail: the demo path in AGENTS.md. This is the safe way to see the pipeline — set up, warm, send, reply, report — before anyone pays for anything.
Going live
Going live is self-serve, not a sales conversation:
curl -s -X POST https://api.coldrig.dev/checkout \
-H 'Authorization: Bearer <your-token>' \
-H 'Content-Type: application/json' \
-d '{"mailboxes":5}'
# → { "mode": "stripe", "url": "https://checkout.stripe.com/...", "sessionId": "cs_..." }
POST /checkout returns a hosted Stripe payment link — open the url and pay. This endpoint is HTTP-only; it is not one of the 34 MCP tools, so an agent operating purely over MCP should hand this step to the owner (or call it directly if the agent has HTTP access). Promotion codes are entered on Stripe's own checkout page, via its "Add promotion code" link — the API deliberately has no coupon-code field of its own. The mailboxes field seeds the initial quote only — the subscription charge actually follows your set-up mailbox count (3-mailbox floor; each mailbox past the first three is $5/month), which then tracks up or down as mailboxes are set up or released. After payment, real mailbox setup is automatic; mailbox send-authorization completes on our side.
Two more HTTP-only routes for a paid account are not MCP tools. POST /billing/portal (no body) returns { url }, a Stripe-hosted billing page where the card on file is updated; it answers 409 no_stripe_customer before the account has gone live and 502 billing_portal_unavailable if the page cannot be opened. POST /cancel takes an optional { immediate } (default false, the end of the billing period). Both are in openapi.yaml, and the dashboard Billing page uses the same routes.
Tool reference
All 34 authed intents, plus the one unauthenticated bootstrap call, signup, which is plain HTTP and not an MCP tool. Full JSON Schemas: openapi.yaml.
| Tool | HTTP | Body / params |
|---|---|---|
signup | POST /signup (no auth) | { brand, contactEmail, vertical? } (vertical: "crowdfunding" joins the shared bounce list: a send it skips shows as a failed event with reason pool_bounce or pool_complaint) |
setup_infrastructure | POST /setup-infrastructure | { brand, primaryDomain, domains, inboxesEach, persona, physicalAddress, senderIdentity } |
infrastructure_status | GET /infrastructure-status | — |
launch_campaign | POST /campaigns | { name, offer, leads[], sequence[], timezone, sendWindow, stopOnReply, endBy, listSource?, previewOnly?, includeAlreadyEmailed? } (endBy and a step's notBefore: paid accounts only; listSource: required on a crowdfunding account; previewOnly: true is a dry run that renders the first contacts' emails and creates nothing). includeAlreadyEmailed: true also stores and emails the contacts who already got this campaign's first email from another campaign of this account; without it they are skipped and counted in alreadyEmailed. To include them, send includeAlreadyEmailed: true on the launch itself (previewOnly: true shows the count first), or after a launch add them to that same campaign with add_leads and includeAlreadyEmailed: true. Never launch the same list again to include them: that emails everyone who was not skipped a second time. With endBy, the answer and the preview also carry endByShortfall, about how many of the contacts it will email will not get their first email before that date (an estimate), and a plain warning sentence when that is above 0 or when other emails, or more people at one company than there are sending days, may still make some miss; nothing is refused for it. By default Coldrig sends Monday–Friday, 8:00–17:00 UTC unless you set timezone; add 6 (Sat) and/or 0 (Sun) to sendWindow.days to send on weekends. |
campaign_results | GET /campaigns/{id}/results | — |
list_campaigns | GET /campaigns | — |
pause | POST /campaigns/{id}/pause | { leadEmails } (optional: stop only these leads, the campaign keeps running). No tool ends a campaign: to stop one for good, pause it and do not resume it |
pause_all | POST /campaigns/pause-all | — |
resume | POST /campaigns/{id}/resume | — (a paused campaign becomes active again; its held steps continue one at a time per lead) |
add_leads | POST /campaigns/{id}/leads | { leads[], listSource?, includeAlreadyEmailed? } (more contacts for an active or paused campaign, from step 1; skips and counts addresses already in it, suppressed addresses and repeats). includeAlreadyEmailed: true also stores and emails the contacts who already got this campaign's first email from another campaign of this account; without it they are skipped and counted in alreadyEmailed. To include them, send includeAlreadyEmailed: true on the launch itself (previewOnly: true shows the count first), or after a launch add them to that same campaign with add_leads and includeAlreadyEmailed: true. Never launch the same list again to include them: that emails everyone who was not skipped a second time. Contacts exist only inside a campaign: there is no separate contacts endpoint |
metrics | GET /metrics | — |
activity | GET /activity | ?limit&cursor&kind=event|deliverability (all optional) |
inbox | GET /inbox | — |
thread | GET /threads/{id} | — |
reply | POST /threads/{id}/reply | { body } |
mark | POST /threads/{id}/mark | { status: "read"|"unread"|"archived" } |
label_thread | POST /threads/{id}/label | { label: string|null } |
account | GET /account | — |
remove_mailboxes | POST /remove-mailboxes | { count, acknowledged: true } |
get_dashboard | GET /dashboard/views, GET /dashboard/views/{id} | — |
configure_dashboard | POST/PUT/DELETE /dashboard/views[/{id}], POST /dashboard/views/{id}/default | { name, layout, note } (per action) |
get_webhooks | GET /webhook-subscriptions, GET /webhook-subscriptions/{id} | — |
configure_webhook | POST /webhook-subscriptions, PUT/DELETE /webhook-subscriptions/{id} | { action, url, eventTypes, secret, active } (per action) |
get_connected_apps | GET /connected-apps | — |
disconnect_app | DELETE /connected-apps/{id} | { id } (signs the app out on its next call) |
get_byo_domains | GET /byo-domains, GET /byo-domains/{id} | — |
configure_byo_domain | POST /byo-domains, POST /byo-domains/{id}/poll-dns, POST /byo-domains/{id}/consent, POST /byo-domains/{id}/managed-mailboxes, POST /byo-domains/{id}/connect-mailbox | { action, domain, domainRelationship, id, acknowledged, count, personaSlug, email, transport } (per action) |
suppress_lead | POST /leads/suppress | { email, reason, note } |
update_lead | POST /leads/disposition | { email, interestStatus, notes, tags } |
list_leads | GET /leads | ?campaign&interestStatus&suppressed&replied&cursor&limit (all optional) |
list_messages | GET /messages | ?limit&cursor (all optional; this account's system and Coldrig-team messages, unacked first) |
ack_message | POST /messages/{id}/ack | — (acknowledge a message so it stops surfacing as unacked) |
contact_operator | POST /messages/contact-operator | { body, urgency } (urgency: normal or needs_human; reach Coldrig support, the reply arrives as a message on this account) |
submit_feedback | POST /messages/feedback | { score, text } (score 0 to 10 and/or text; at least one) |
plan_campaign | POST /campaigns/plan | { contacts, campaignStart, campaignDays, bookingDate, domainsAlreadyHeld, listIsOwnOptIn } (read-only plan and price quote; booking is not available, contact support) |
Webhook boundary: real push webhooks are live. get_webhooks/configure_webhook deliver reply, bounce, soft_bounce, complaint, and unsubscribe events (HMAC-SHA256 signed, header X-Coldrig-Signature) to your own HTTPS endpoint; URLs must be a public HTTPS host. activity remains available as a pollable alternative for the same event classes.
BYO domain boundary: domain registration, the pre-flight scan, and consent tracking via get_byo_domains/configure_byo_domain are live, reachable tools today. Real DNS delegation and mailboxes Coldrig sets up on your own domain are self-serve and automatic through setup; mailbox send-authorization completes on our side — see the BYO domain page.
Auth model
One API key per account, minted at signup. Every authed call sends it as a bearer token (the same key in an X-API-Key header also works, for gateways that reserve Authorization):
Authorization: Bearer <token>
The hosted MCP endpoint also accepts OAuth: an app that supports it sends the person through a Coldrig sign-in and an Allow page, then calls the MCP tools with exactly that one account's access, until the app is disconnected in Settings or the key is rotated. OAuth works on the MCP endpoint only, not on the HTTP routes.
The token resolves server-side to exactly one account's separate state, and every engine query is additionally scoped by account ID as a second, independent check. The one exception is the crowdfunding shared bounce list: it holds only hashed addresses that hard-bounced or complained, with no customer identity attached.