Docs

Quickstart, MCP setup, CLI usage, and the full tool reference — written for both a human setting this up and an agent operating it.

Live in production The hosted MCP endpoint, HTTP API, npm CLI, fault-injecting sandbox, and real sending (Gmail API, HTTPS/443) are all live. A public signup by default creates an isolated demo tenant that cannot reach vendor adapters; signup, billing, screening, and real mailbox provisioning on your own account are fully self-serve and automatic — the only remaining step is mailbox send-authorization completing on our side after provisioning, and you don't wait on a queue or do anything.
On this page

Quickstart

  1. 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": "..." }
  2. Hand the token to your agent — via MCP config, CLI env var, or directly as a bearer token if your agent talks HTTP.
  3. Have your agent call setup_infrastructure, then launch_campaign; infrastructure_status shows 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, no OAuth dance. It is live in production; demo tenants run against sandbox vendors, and activated tenants reach real ones — see the status banner above.

CLI usage

npx agent-cold-email demo                 # run the full pipeline against the live sandbox, 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 tenant 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 provisioning step, warmup tick, and send in demo mode runs against a fault-injecting sandbox 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 sandbox 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 — provision, 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 32 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 provisioned mailbox count (5-mailbox floor; each mailbox beyond is $8/month through 20, $7 after that), which then tracks up or down as mailboxes provision or are released. After payment, real mailbox provisioning is automatic; the only remaining step is mailbox send-authorization completing on our side.

Tool reference

All 32 authed intents, plus the one unauthenticated bootstrap call, signup, which is plain HTTP and not an MCP tool. Full JSON Schemas: openapi.yaml.

ToolHTTPBody / params
signupPOST /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_infrastructurePOST /setup-infrastructure{ brand, primaryDomain, domains, inboxesEach, persona, physicalAddress, senderIdentity }
infrastructure_statusGET /infrastructure-status—
launch_campaignPOST /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, at least how many of the contacts it will email cannot get their first email before that date (a guaranteed minimum), 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.
campaign_resultsGET /campaigns/{id}/results—
list_campaignsGET /campaigns—
pausePOST /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_allPOST /campaigns/pause-all—
resumePOST /campaigns/{id}/resume— (a paused campaign becomes active again; its held steps continue one at a time per lead)
add_leadsPOST /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
metricsGET /metrics—
activityGET /activity?limit&cursor&kind=event|deliverability (all optional)
inboxGET /inbox—
threadGET /threads/{id}—
replyPOST /threads/{id}/reply{ body }
markPOST /threads/{id}/mark{ status: "read"|"unread"|"archived" }
label_threadPOST /threads/{id}/label{ label: string|null }
accountGET /account—
remove_mailboxesPOST /remove-mailboxes{ count, acknowledged: true }
get_dashboardGET /dashboard/views, GET /dashboard/views/{id}—
configure_dashboardPOST/PUT/DELETE /dashboard/views[/{id}], POST /dashboard/views/{id}/default{ name, layout, note } (per action)
get_webhooksGET /webhook-subscriptions, GET /webhook-subscriptions/{id}—
configure_webhookPOST /webhook-subscriptions, PUT/DELETE /webhook-subscriptions/{id}{ action, url, eventTypes, secret, active } (per action)
get_byo_domainsGET /byo-domains, GET /byo-domains/{id}—
configure_byo_domainPOST /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_leadPOST /leads/suppress{ email, reason, note }
update_leadPOST /leads/disposition{ email, interestStatus, notes, tags }
list_leadsGET /leads?campaign&interestStatus&suppressed&replied&cursor&limit (all optional)
list_messagesGET /messages?limit&cursor (all optional; this account's system and operator messages, unacked first)
ack_messagePOST /messages/{id}/ack— (acknowledge a message so it stops surfacing as unacked)
contact_operatorPOST /messages/contact-operator{ body, urgency } (urgency: normal or needs_human; reach a human operator, the reply arrives as a message on this account)
submit_feedbackPOST /messages/feedback{ score, text } (score 0 to 10 and/or text; at least one)
plan_campaignPOST /campaigns/plan{ contacts, campaignStart, campaignDays, bookingDate, domainsAlreadyHeld, listIsOwnOptIn } (read-only plan and price quote; booking is not available yet, contact support)

Two optional helpers (write_sequence, suggest_domains) are designed but not yet built — do not assume they exist today.

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, live reputation checks, and platform-provisioned mailboxes on your own domain are self-serve and automatic through provisioning; mailbox send-authorization still completes on our side — see the BYO domain page.

Auth model

One bearer token per tenant, minted at signup. Every authed call:

Authorization: Bearer <token>

The token resolves server-side to exactly one tenant's isolated state, and every engine query is additionally scoped by tenant 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.