Every change Wellknown observed on this MCP server, newest first, with what it was before and what it became. Tool-surface changes carry the definition diff. Nothing here is edited after the fact.
Changed the definition of "setup_infrastructure"
⟨28 unchanged words⟩ "minimum":1,"type":"integer"},"dotComDomains":{"description":"Only when the quote's billing.domainEnding is set: true keeps those new domains on .com for the one-time fee it shows per domain, added to the next bill. Leave it out for .co. Ask the account owner first.","type":"boolean"},"idempotencyKey":{"description":"Optional idempotency key: ⟨42 unchanged words⟩ ":500,"minLength":1,"type":"string"},"prepaySecondMonth":{"description":"Only when the quote's billing.prepay is set (an order making more than 4 new domains in your first billing month): true accepts paying month 2 up front with this order (the amount it shows, charged right after the mailboxes are set up; the next renewal moves one month later; a cancel at any time ends the plan on that date; the prepaid month is not refunded). Ask the account owner first.","type":"boolean"},"primaryDomain":{"maxLength":253,"minLength ⟨124 unchanged words⟩
Changed the definition of "account", "configure_byo_domain", "contact_operator" and 2 more
⟨47 unchanged words⟩ most 20 Coldrig-bought domains, with at most105 mailboxes on each), deliverability (paused/throttled mailboxes, ⟨29 unchanged words⟩ is per mailbox, on the live mailbox count: $49 platform + per mailbox,graduated: 1-5at$10, 6-20 at $8,the21+priceatand$7;minimum5-mailboxinminimum`pricing`
Authorization required, Bearer, issuer https://api.coldrig.dev
Changed the definition of "plan_campaign"
Plan a dated outreach campaign (for examplee.g. a fundraising drive) and get its price. ⟨12 unchanged words⟩ day, YYYY-MM-DD, UTC), campaignDays, bookingDate (the day you would book,normally today), and optionally domainsAlreadyHeld (default 0; ⟨25 unchanged words⟩ contacts the warm-up ramp can safely reach ifyou bookbooked on bookingDate; the price is for that many. warnings: late_booking (safeReachisbelow contacts), own_list_cheaper_on_esp (an opted-in list may cost less on an email-service provider), campaign_day_not_sent_by_default (weekend day: in reach and price, sent only if sendWindow.days at launch has 6 for Sat and/or 0 for Sun). A bookingDate after the last campaign day is refused 400.Campaign checkout and bookingBookingareis
Changed the definition of "account", "ack_message", "activity" and 14 more
⟨13 unchanged words⟩ billingState, activationState, resource counts, usageCents, quota (the most domains and mailboxes a test-mode account can set up; null on a paid account,deliverabilitywhich(loophasstate:no such quota, though its setup_infrastructure layout still names at most 20 Coldrig-bought domains, with at most 10 mailboxes on each), deliverability (paused/throttled mailboxes, burning domains, auto-replacements, recentActions[] ⟨18 unchanged words⟩ campaigns stop sending; else null). Billing isper-provisioned-mailboxper mailbox, on the liveprovisionedmailbox
Authorization not required → required, Bearer, issuer https://api.coldrig.dev
Changed the definition of "account"
⟨24 unchanged words⟩ , burning domains, auto-replacements, recentActions[]),andteardown (reclaim summary once canceled, else null), and sendingStopsAt (after an end-of-period cancel: the ISO UTC time running campaigns stop sending; else null). Billing is per-provisioned-mailbox on the ⟨93 unchanged words⟩
Added "disconnect_app" and "get_connected_apps"; changed the definition of "account", "plan_campaign" and "suppress_lead" (32 tools before, 34 now)
⟨74 unchanged words⟩ (deprovision lowers it). activationState is theHONESTactual send state — trust it over 'sent' counts ⟨45 unchanged words⟩
⟨119 unchanged words⟩ 400. Campaign checkout and booking are not availableyet: to book, contact support (contact_operator).
Added "disconnect_app" and "get_connected_apps"; changed the definition of "account", "plan_campaign" and "suppress_lead" (32 tools before, 34 now)
⟨74 unchanged words⟩ (deprovision lowers it). activationState is theHONESTactual send state — trust it over 'sent' counts ⟨45 unchanged words⟩
⟨119 unchanged words⟩ 400. Campaign checkout and booking are not availableyet: to book, contact support (contact_operator).
Certificate changed, valid to 2027-01-08
Added "add_leads" and "resume"; changed the definition of "campaign_results", "inbox", "launch_campaign" and 6 more (30 tools before, 32 now)
⟨13 unchanged words⟩ reply, bounce, complaint, unsubscribe, failed, soft_bounce, auto_reply } — event counts, bounce = HARD only, soft_bounce separate, opens nottracked.tracked; auto_reply = automatic replies such as out-of-office, which are NOT replies (never in reply, replied, replyRate or positiveReplies; they never stop a sequence) — plus unique-contact numbers: contacts (leads in the campaign), contacted (leads sent at least once), replied (leads that replied), replyRate (replied / contacted, 0..1), positiveReplies (replied leads marked interested or meeting_booked via update_lead), and perStep[] of { step, sent, contacted, replied, bounce, unsubscribe } ordered by step (a reply, bounce or unsubscribe counts toward the lead's latest sequence step sent before it). 404 if unknown. To see each contact's own timeline use list_leads with campaign, then thread. Use metrics for account-wide totals, list_campaigns for every campaign at once.
Added "add_leads" and "resume"; changed the definition of "campaign_results", "inbox", "launch_campaign" and 6 more (30 tools before, 32 now)
⟨13 unchanged words⟩ reply, bounce, complaint, unsubscribe, failed, soft_bounce, auto_reply } — event counts, bounce = HARD only, soft_bounce separate, opens nottracked.tracked; auto_reply = automatic replies such as out-of-office, which are NOT replies (never in reply, replied, replyRate or positiveReplies; they never stop a sequence) — plus unique-contact numbers: contacts (leads in the campaign), contacted (leads sent at least once), replied (leads that replied), replyRate (replied / contacted, 0..1), positiveReplies (replied leads marked interested or meeting_booked via update_lead), and perStep[] of { step, sent, contacted, replied, bounce, unsubscribe } ordered by step (a reply, bounce or unsubscribe counts toward the lead's latest sequence step sent before it). 404 if unknown. To see each contact's own timeline use list_leads with campaign, then thread. Use metrics for account-wide totals, list_campaigns for every campaign at once.
Added "plan_campaign" and "submit_feedback"; changed the definition of "account", "launch_campaign" and "pause" (28 tools before, 30 now)
Account overview: brand, plan, status, vertical ('crowdfunding' or null — set at signup), billingState, activationState, resource counts, usageCents, quota, ⟨116 unchanged words⟩
⟨89 unchanged words⟩ conversation), and optionally timezone, sendWindow, stopOnReply, listSource (one sentence on where the list came from — REQUIRED on a crowdfunding account, where a missing or "unknown" source is refused 400) — the platform does not write copy. Each step's ⟨448 unchanged words⟩
Added "plan_campaign" and "submit_feedback"; changed the definition of "account", "launch_campaign" and "pause" (28 tools before, 30 now)
Account overview: brand, plan, status, vertical ('crowdfunding' or null — set at signup), billingState, activationState, resource counts, usageCents, quota, ⟨116 unchanged words⟩
⟨89 unchanged words⟩ conversation), and optionally timezone, sendWindow, stopOnReply, listSource (one sentence on where the list came from — REQUIRED on a crowdfunding account, where a missing or "unknown" source is refused 400) — the platform does not write copy. Each step's ⟨448 unchanged words⟩
Changed the definition of "configure_byo_domain", "contact_operator", "infrastructure_status" and 4 more
Register or advance a BYO domain/mailboxintake (SPEC.md §20).intake. action = register (needs domain + domainRelationship: fresh_standalone|subdomain_of_primary|is_primary — runsthepre-flightlive-infrascan + abuse gate + reputation ladder, returnsthestarting byoStatus) | poll_dns (needs id — re-checks DNSdelegation/recordsdelegation, advances pending_dns→-> active, or→-> abandoned after 7 idle days) | acknowledge_consent (needs id + acknowledged:true —REQUIREDrequired before a primary domain can proceed past pending_consent;this
Changed the definition of "configure_byo_domain", "contact_operator", "infrastructure_status" and 4 more
Register or advance a BYO domain/mailboxintake (SPEC.md §20).intake. action = register (needs domain + domainRelationship: fresh_standalone|subdomain_of_primary|is_primary — runsthepre-flightlive-infrascan + abuse gate + reputation ladder, returnsthestarting byoStatus) | poll_dns (needs id — re-checks DNSdelegation/recordsdelegation, advances pending_dns→-> active, or→-> abandoned after 7 idle days) | acknowledge_consent (needs id + acknowledged:true —REQUIREDrequired before a primary domain can proceed past pending_consent;this
Changed the definition of "account" and "setup_infrastructure"
⟨43 unchanged words⟩ : 1-5 at $10, 6-20 at $8,21-100 at $6, 101+21+ at$5;$7; 5-mailbox minimum ($99 at 5); the ⟨68 unchanged words⟩
⟨62 unchanged words⟩ : 1-5 at $10, 6-20 at $8,21-100 at $6, 101+21+ at$5;$7;
Changed the definition of "account" and "setup_infrastructure"
⟨43 unchanged words⟩ : 1-5 at $10, 6-20 at $8,21-100 at $6, 101+21+ at$5;$7; 5-mailbox minimum ($99 at 5); the ⟨68 unchanged words⟩
⟨62 unchanged words⟩ : 1-5 at $10, 6-20 at $8,21-100 at $6, 101+21+ at$5;$7;
Changed the definition of "account" and "setup_infrastructure"
⟨24 unchanged words⟩ once canceled, else null). Billing is per-provisioned-mailbox on the live provisioned count: $49 platform + per mailbox, graduated: 1-5 at $10,x6-20liveatprovisioned$8,mailboxes21-100 at $6,minimum101+5at $5; 5-mailbox minimum ($99 at 5); the billed quantity tracks the real provisioned
Changed the definition of "account" and "setup_infrastructure"
⟨24 unchanged words⟩ once canceled, else null). Billing is per-provisioned-mailbox on the live provisioned count: $49 platform + per mailbox, graduated: 1-5 at $10,x6-20liveatprovisioned$8,mailboxes21-100 at $6,minimum101+5at $5; 5-mailbox minimum ($99 at 5); the billed quantity tracks the real provisioned
Changed the definition of "launch_campaign"
⟨94 unchanged words⟩ stopOnReply — the platform does not write copy. Each step's `step` number must be unique within the sequence; a duplicate step number is refused (400) naming it. Subjects and bodies may use {{firstName}} and {{company}} (filled from each lead); a launchusingis refused (400) if any subject or body uses any other {{token}},isarefused.single-brace form of a known token such as {firstName} or {company} (a typo for the double-brace form — it would otherwise reach the recipient unsubstituted), or a run of 3 or more consecutive `{` or `}` anywhere (e.g. {{{firstName}}} or a lopsided run like {{{firstName}} — it would otherwise reach the recipient with stray braces attached). The send window is evaluated in `timezone` ( ⟨355 unchanged words⟩
Changed the definition of "account", "launch_campaign" and "setup_infrastructure"
⟨87 unchanged words⟩ DON'T leave; 'capacity_pending' = provisioning held at aspend/plan-slotspend limit; 'screening_hold' = account under review; 'sandbox' = ⟨9 unchanged words⟩
⟨15 unchanged words⟩ [] (per step: subject, body, delayDays — subject is REQUIRED on step 1; a LATER step may omit it to keep step 1's subject line [recommended], which resolves at launch to "Re: " + step 1's subject (unchanged if that already starts with "Re: "),sendWindowor set one explicitly to change only that step's subject line — every later step is still sent In-Reply-To step 1
Changed the definition of "account", "launch_campaign" and "setup_infrastructure"
⟨87 unchanged words⟩ DON'T leave; 'capacity_pending' = provisioning held at aspend/plan-slotspend limit; 'screening_hold' = account under review; 'sandbox' = ⟨9 unchanged words⟩
⟨15 unchanged words⟩ [] (per step: subject, body, delayDays — subject is REQUIRED on step 1; a LATER step may omit it to keep step 1's subject line [recommended], which resolves at launch to "Re: " + step 1's subject (unchanged if that already starts with "Re: "),sendWindowor set one explicitly to change only that step's subject line — every later step is still sent In-Reply-To step 1
Certificate recorded, valid to 2026-12-24
Authorization not required
First tool surface recorded: 28 tools (server version 0.2.2)
https://api.coldrig.dev/mcp (mcp_streamable_http) — from mcp_registry
Certificate recorded, valid to 2026-11-10
Authorization not required
First tool surface recorded: 28 tools (server version 0.2.2)
Showing the latest 30 events. The API returns up to 500 and filters by kind: ?kind=tool_surface_changed
⟨56 unchanged words⟩ monthly bill: ask the account owner first). At most 5; on a live account, a domain with no managed mailboxes needs 3-5.","maximum":105,"minimum":1,"type":"integer"}, ⟨259 unchanged words⟩
Reacha human atColdrig support for anything list_messages/infrastructure_status cannot answer (a ⟨132 unchanged words⟩
⟨28 unchanged words⟩ credit, lower price starts next renewal, minimum:5seemailboxes/$99account pricing). Returns { releasedCount, failedCount, unreleased, ⟨108 unchanged words⟩
⟨52 unchanged words⟩ `domains`/`inboxesEach` are the setup you want to HAVE, not an amount to add: a repeat call never buys twice; each ordinal 0..domains-1 fills to its owncount.count, at most 5 (3-5 for a live account's new domain). Mailbox addresses are DETERMINISTIC from persona+ordinal+slot: keep persona ⟨38 unchanged words⟩ NO background retry exists: a `provisioning` result(pending|capacity_pending)of pending needs YOU to call again; capacity_pending needscontact_operator instead.contact_operator. Returns { jobId, billing, provisioning?, nextSteps }.
⟨11 unchanged words⟩ },"distribution":{"items":{"maximum":105,"minimum":1,"type":"integer"}, ⟨43 unchanged words⟩ ":"string"},"inboxesEach":{"maximum":105,"minimum":1,"type":"integer"}, ⟨142 unchanged words⟩
⟨59 unchanged words⟩ 404 if the id doesn't exist for thistenant.account.
Unified activity feed: campaign events (sent/reply/bounce/...) merged with deliverabilityloopactions (pause/throttle/replace-domain). Cursor-paginated → { items[] ⟨25 unchanged words⟩
Register or advance a BYOdomain/mailboxdomain intake. action = register (needs domain + domainRelationship: fresh_standalone|subdomain_of_primary|is_primary — runs pre-flight scan + abuse gate + reputation ladder, returns starting byoStatus) | poll_dns (needs id — re-checks DNSdelegation, advancesdelegation;pending_dnsmoves->to active, or->to abandoned after 7 idle days) | acknowledge_consent (needs id + acknowledged:true, only after the account owner agrees — required before a primary domaincan proceed pastpasses pending_consent; documents informedconsent, does not removeconsent; exposure remains) | request_managed_mailboxes (needs id + count —platform-provisionedmanaged mailboxes on an ALREADY-ACTIVE domain; on a live account this raises the bill: ask the account owner, and run it only after they approve the price; every response carries a billing projection { provisionedAfter, projectedMonthlyCents, formula }; quoteOnly:true previews withoutprovisioningadding any) | connect_mailbox (needs id + email + transport — declares an EXISTINGOAuth/SMTP+IMAP connection, bypassing provisioning;connection; transport is smtp | gmail_api | ms_graph, each with its owncredential fieldscredentials).
⟨8 unchanged words⟩ ":"Required (must be true) for acknowledge_consent,—andSPEC.mdonly§20.4'safterseparate,theunbundledaccount owner has agreed: the separate risk acknowledgment.","type":"boolean"},"action ⟨9 unchanged words⟩ description":"Required for request_managed_mailboxes — how manyplatform-provisionedColdrig-managed mailboxes toattach.add (on a live account this raises the monthly bill: ask the account owner first).","maximum":10,"minimum":1,"type ⟨101 unchanged words⟩ new mailbox count + projected monthly price WITHOUTprovisioningadding any (SPECquote§18before
⟨20 unchanged words⟩ → default) | delete (id). updateischecksrev-CASthe rev: a stale rev returns { currentRev, currentLayout }tosorebaseyou can re-apply your change and retry. Optional note. Read the current rev+layout via get_dashboard first.
Reach a humanoperatorat Coldrig support for anything list_messages/infrastructure_status cannot answer (a stuck vendor ⟨14 unchanged words⟩ default normal). Files a ticket, notifiestheColdrigoperator;support; returns { ticketId, note, deduplicated }. ⟨24 unchanged words⟩ as a message on THIS account (polllist_messages/infrastructure_status —list_messages; no reply-fetch call). Sending the IDENTICAL body+urgency ⟨44 unchanged words⟩ `needs_human` bypasses the ~10-min ops-email throttle. Rate-limited to5/hour/tenant5/hour per account — a 429 names retryAfter (seconds).
{"destructiveHint":false,"title":"ContactOperatorColdrig Support"}
⟨22 unchanged words⟩ + rev (pass this rev as theCASbasetowhenconfigure_dashboardyou update with configure_dashboard). Views are both agent- and human-editable; write them with configure_dashboard.
Warmup+andprovisioningsetup progress per mailbox. Returns { domains, mailboxes, ⟨50 unchanged words⟩ capacity. Top-level `sendReady` is the AND across ALLmailboxes (true once every one is ramped) —mailboxes.forEachonemailboxmailbox'scarriescapacitystatususe(warming/active,itsorownrestingflag.afterEachsittingmailboxunused)carriesand delivStatus (healthy/throttled/paused), complaint/bounce/softBounce rates; vendorReputationScore/vendorPlacementRate are VENDOR-REPORTED, null when unmeasured.Vendor-poolWarmupwarmuprun in the mailbox provider's own pool isFEED-INVISIBLENOT shown here, by design. `messages[]` previews 5 unacked ⟨20 unchanged words⟩
⟨8 unchanged words⟩ list. You supply name, offer, leads[],and sequence[] (per step: step, subject, body, delayDays— subject is REQUIRED on step 1; a LATER step);maytheomitplatformitdoestonotkeepwritestepcopy.1'sOptional:subjecttimezone,linesendWindow,[recommended]stopOnReply,whichlistSourceresolves(oneatsentencelaunchontowhere"Re:the"list+camestepfrom;1'sREQUIREDsubjecton(unchangedaifcrowdfundingthataccount,alreadywherestartsawithmissing"Re:or "), orunknown"setsourceoneisexplicitlyrefusedto400).changeSteponlynumbersthatmuststep'sbesubjectunique.lineSubject—iseveryREQUIREDlateron stepis still1;sentaIn-Reply-Tolater step1, thoughmaysomeomitclientstodisplayreuseastepchanged1's subject asa separate conversation), and optionally"Re:timezone,"sendWindow,+stopOnReply,thatlistSourcesubject (one sentence on where the list came from — REQUIRED on a crowdfunding accountrecommended),where a missingor"unknown" source is refused 400) — the platformsetdoesitsnotown,writethoughcopy.someEachmailstep'sclients`step`shownumberamustchangedbesubjectuniqueaswithinatheseparatesequence;conversation;aeveryduplicatelater stepnumberisrefusedstill(400)sentnamingIn-Reply-Toit.step 1. Subjects and bodies may use {{firstName}} and {{company}} (filled from each lead;firstName and company areboth optional) plus your own {{key}} for any key every lead carries in its optional `vars` map (word-keys to one-line strings, e.g. vars:{ icebreaker:"..." };a key may not be firstName or company). A;valuevaluesisare insertedonceas plain text. A launch is refused (400 ⟨4 unchanged words⟩ how many leads lack it, if asubject or body uses {{firstName}} or a {{key}} that any leadtoken has no non-empty value for— no fallback word is invented;someitlead,isand alsorefusedifany subject or body usesa {{…...}}thatis not one word, a single-braceform of a known token such as{firstName} or {company}(a typo for the double-brace form — it would otherwise reach the recipientisunsubstituted)used, ora run of 3three or moreconsecutive `{`or`}`anywhere (e.g. {{{firstName}}} or a lopsidedrunlike {{{firstName}} —together:iteach wouldotherwisereach the recipientwith stray braces attached).unfilled. The send window is evaluated in `timezone` (IANA, e.g. America/New_York; default UTC)—: set it to your recipients' zone. sendWindow is { startHour, endHour, days? }:integerhours 0-23, endHour INCLUSIVE (the last hour a send may start in), days 0=Sunday…... 6=Saturday.Each omitted field (or all of them, by omittingOmittedsendWindow)fieldsgetsget theplatform'sRECOMMENDED default {"startHour":8,"endHour":16,"days":[1,2,3,4,5]}—(Monday-Friday business hours—), except on asandboxtest-mode account, whereomitted fieldsthey are open every hour of every day until itupgrades and they take thatgoesdefault;live; the decision is yours, e.g. include 0 ⟨5 unchanged words⟩ send on weekends. Setting exactly ONE of startHour/endHourcombinespairs it with the recommendeddefaultvalue for theOTHERother (08/16,includingon asandboxtest-mode account,since that value re-resolves to the same default on upgradetoo);—aiflaunch thatcombinationwould wrapthe windowpast midnight(start >isend)refused,the launch is refused;so set bothexplicitly, includingfora deliberatean overnight window such as {startHour:22, endHour ⟨19 unchanged words⟩ step ACTUALLY sent, from the SAME mailbox,with In-Reply-To/References set toIn-Reply-To step1's Message-ID —1,itand waits while that mailbox is at its dailycap, andcap.theA lead's remaining steps are cancelled (a 'failed' event says why) once that mailbox is paused or released,since neither ever lifts on its own,or when a paid account's thread went out from a connected BYO mailbox, whichthis buildColdrig never sends from; a step whose previous step never went out is skipped. Lead addresses are trimmed andlower-cased, and anlower-cased;addressa repeatedin leads[]address is keptonce (its first row).once;Suppressedsuppressed leads are skipped. Returns { campaignId, sendWindow, ⟨3 unchanged words⟩ duplicatesRemoved, alreadyEmailed, endByShortfall, warning, nextSteps }—: sendWindow and timezone are what actually applied; contacts ⟨11 unchanged words⟩ are on your suppression list (never scheduled), duplicatesRemoved the repeated rows dropped, alreadyEmailed the leads left out because they already got this email.. With an endBy, endByShortfall isat leastabout how manyof thecontactsthis campaignwillemail cannotnot get their first email before endBy onthisthe account's currentmailboxes (a guaranteed minimum: each mailbox's daily cap on each day, warmup and throttle included, on the sendWindow's days and hours in timezone, and on a paid account at most one email per recipient domain in each 5-minute send round, counted as if nothing else were sending), andmailboxes; warning is one plain sentencesaying sowhen it is above 0,or(asayingshortfall,thator othercampaigns'waiting emails,this campaign's own follow-ups, or more people at one company than there are sending days before endBythat may still make some contacts miss endBy): tell the person before they confirm. Nothing ⟨13 unchanged words⟩ is a dry run: every launch check runs(a launch that would be refused is refused the same way), nothing is created or sent, no idempotencyKey ⟨10 unchanged words⟩ duplicatesRemoved, alreadyEmailed, endByShortfall, warning, samples }— the same endByShortfall and warning the launch would carry;where samples[]is= { email, steps[] } for the first three leads,(eachstep itsstep's rendered subject andbodybody;(every real send then appends the required footer: ⟨480 unchanged words⟩
⟨176 unchanged words⟩ retry is not applied twice (no duplicatecampaign/provision/sendcampaign/setup/send).","maxLength":200,"minLength":1, ⟨388 unchanged words⟩ 3,4,5]} — except on asandboxtest-mode account, where omitted fields are open every hour of every day until itupgradesgoes live and they take the recommended default. If you ⟨62 unchanged words⟩ 5] (every day while the account isainsandboxtest mode); include 0 and 6 to send on ⟨349 unchanged words⟩
List thistenant'saccount's system+andoperatorColdrig support messages (a retryable setup step, a credential going live,anaoperatornotenoticefrom Coldrig support), cursor-paginated. Unacked messages sort first (newest ⟨29 unchanged words⟩ progresses | 'operator_pending' = platform stopped, onlyanColdrigoperatorsupport can restart it, retrying the SAME call ⟨66 unchanged words⟩
⟨15 unchanged words⟩ the same event counts as campaign_results but summedtenant-wideacross the whole account (no per-contact fields) (bounce = hard ⟨27 unchanged words⟩
Pause EVERY active campaignforon thetenantaccount at once (each active status → 'paused'; ⟨36 unchanged words⟩
⟨80 unchanged words⟩ the price is for that many. warnings:over_60_mailboxes (above the 60-address self-serve ceiling),late_booking (safeReach is below contacts), own_list_cheaper_on_esp ( ⟨34 unchanged words⟩
⟨8 unchanged words⟩ the billed quantity. Inputs: count, acknowledged (musttruebeonlytrueafter—the account owner agrees: release is immediatebutand irreversible; no mid-cycle credit, lower price starts ⟨9 unchanged words⟩ unreleased, billing, deduplicated }. `count` is RELATIVE(that many MORE, not a fleettarget).target. ALWAYS pass an idempotencyKey: the FIRST call ⟨20 unchanged words⟩ `releasedCount` can be less than `count` — `failedCount`namesis how many the vendor refused (still live, billed; `unreleased` lists them).ResendRepeat the call with the same key until `failedCount` is 0. `deduplicated: true` = NO newwork —work; an earlierrecordedoutcome under that key was replayed. A genuinely NEW downgrade needs a NEW key. 409 = a release is already running;re-checkcheck infrastructure_statusbefore retrying.first. To ADD mailboxes use setup_infrastructure or configure_byo_domain.
⟨32 unchanged words⟩ retry is not applied twice (no duplicatecampaign/provision/sendcampaign/setup/send).","maxLength":200,"minLength":1, ⟨7 unchanged words⟩
⟨27 unchanged words⟩ retry is not applied twice (no duplicatecampaign/provision/sendcampaign/setup/send).","maxLength":200,"minLength":1, ⟨22 unchanged words⟩
ProvisionSetsendingupinfrasending: buy lookalike domains, create mailboxes, start warmup.Inputs:Onbrand,aprimaryDomain,livedomainsaccount+thisinboxesEachspends(ormoneydistribution),andpersona,raisesphysicalAddressthe monthly bill,senderIdentity.soBillingALWAYSisaskpertheprovisionedaccountmailbox;owner first: get the price with quoteOnly:true,previewsshowcostit,first.and run it for real only after the owner approves that price in this conversation. `domains`/`inboxesEach` are theinfrasetup you want to HAVE, not an amount to add—: a repeat call never buys twice; each ordinal 0..domains-1 fills to its own count. Mailbox addresses are DETERMINISTIC from persona+ordinal+slot—: keep persona unchanged on a retry.`registerDomains:true` isBuyingrequiredaconsentNEWbeforedomainanyalsoNEWneedsdomain`registerDomains:true`purchase;(andomitting`registrant`,itunless ona buyfile),callsetisonlyrefusedafter400theregistrar_optin_missingaccount(self-correctownerbyapprovesresendingthattrue,purchase;neverwithoutanitoperatortheescalation)call—isalsorefusedneeds400`registrant`,registrar_optin_missingunlessandalreadynothingonisfile.bought. NO background retry exists: a `provisioning` result (pending|capacity_pending) needs YOU toretry tocallprogress;again; capacity_pending needs contact_operator instead. Returns { jobId, ⟨4 unchanged words⟩
⟨48 unchanged words⟩ retry is not applied twice (no duplicatecampaign/provision/sendcampaign/setup/send).","maxLength":200,"minLength":1, ⟨151 unchanged words⟩
{"destructiveHint":falsetrue,"title":"Set Up Sending Infrastructure"}
Permanently suppress an email addresstenant-wideaccount-wide (every current and future campaign) — the ⟨59 unchanged words⟩
⟨28 unchanged words⟩ fixed 'manual' — the only value this toolhonestlyclaims; bounce/complaint/unsubscribe are recorded automatically elsewhere), note ⟨29 unchanged words⟩
Disconnect one app connected with "Sign in with Coldrig". Input: id from get_connected_apps. The app is signed out on its next call and cannot refresh its access; webhooks it created keep running until you delete them (configure_webhook). Disconnecting the app that is making this call is allowed: this call still answers, then that app is signed out. Unknown id: not found. Returns { disconnected: true }.
List the apps connected to this account with "Sign in with Coldrig" (for example ChatGPT or Claude): [{ id, name, redirectHost, connectedAt }]. name is what the app called itself and is not verified; redirectHost is where its sign-in returned; connectedAt is when it connected (epoch ms). API keys are not listed. Read-only.
⟨28 unchanged words⟩ fixed 'manual' — the only value this toolhonestlyclaims; bounce/complaint/unsubscribe are recorded automatically elsewhere), note ⟨29 unchanged words⟩
Disconnect one app connected with "Sign in with Coldrig". Input: id from get_connected_apps. The app is signed out on its next call and cannot refresh its access; webhooks it created keep running until you delete them (configure_webhook). Disconnecting the app that is making this call is allowed: this call still answers, then that app is signed out. Unknown id: not found. Returns { disconnected: true }.
List the apps connected to this account with "Sign in with Coldrig" (for example ChatGPT or Claude): [{ id, name, redirectHost, connectedAt }]. name is what the app called itself and is not verified; redirectHost is where its sign-in returned; connectedAt is when it connected (epoch ms). API keys are not listed. Read-only.
⟨21 unchanged words⟩ mailbox, campaign, label, read, includeNonreply (bounces/OOOthreads whose last event is a bounce, complaint or automatic reply such as out-of-office; default true), archived (exclude|include|only). Use ⟨5 unchanged words⟩
⟨151 unchanged words⟩ and {{company}} (filled from eachleadlead; firstName and company are both optional) plus your own {{key}} for any key every lead carries in its optional `vars` map (word-keys to one-line strings, e.g. vars:{ icebreaker:"..." }; a key may not be firstName or company). A value is inserted once as plain text. A launch is refused (400), naming the token and how many leads lack it, ifanya subject or body uses {{firstName}} or a {{key}} that anyotherlead has no non-empty value for — no fallback word is invented; it is also refused if any subject or body uses a {{token…}} that is not one word, a single-brace form of a known token ⟨314 unchanged words⟩ whose previous step never went out is skipped. Lead addresses are trimmed and lower-cased, and an address repeated in leads[] is kept once (its first row). Suppressed leads are skipped. Returns { campaignId, sendWindow, timezone, contacts, suppressed, duplicatesRemoved, alreadyEmailed, endByShortfall, warning, nextSteps } — sendWindow and timezone are what actuallyapplied.applied;Campaignscontacts is how many leads were stored, suppressed how many of those are on your suppression list (never scheduled), duplicatesRemoved the repeated rows dropped, alreadyEmailed the leads left out because they already got this email. With an endBy, endByShortfall is at least how many of the contacts this campaign will email cannot get their first email before endBy on this account's current mailboxes (a guaranteed minimum: each mailbox's daily cap on each day, warmup and throttle included, on the sendWindow's days and hours in timezone, and on a paid account at most one email per recipient domain in each 5-minute sendrealround,identicalchecktorunsone(a launch that would be refused is refused the same way), nothing is created or sent, no idempotencyKey is recorded, and it returns { preview, sendWindow, timezone, contacts, suppressed, duplicatesRemoved, alreadyEmailed, endByShortfall, warning, samples } — the same endByShortfall and warning the launch would carry; samples[] is { email, steps[] } for the first three leads, each step its rendered subject and body (every real send then appends the required footer: sender identity, postal address, unsubscribe link). People who already got this email are skipped: a contact is left out, never stored or emailed, when another campaign of this accountmadehas sent them, or still has waiting for them, an email with the same subject and body as this campaign's first email, as written before {{tokens}} are filled in (spacing differences do not count). A trial's test email counts only while thelastaccount60issecondsstill on the trial, and the built-in demo campaign never counts. alreadyEmailed in the answer isREFUSEDhowwithmany409were{skippedcode(anyone also on your suppression list is counted there, not in suppressed):'duplicate_campaign'tell the person in plain words,existingCampaignIdfor}examplerather"skippedthan12contactingpeople who already got this email". To include them, send includeAlreadyEmailed: true on the launch itself (previewOnly: true shows the count first), or after a launch add them to that sameprospectscampaigntwicewith—add_leadscheckand includeAlreadyEmailed: true. Never launch the same list again to include them: that emails everyone who was not skipped a second time. Naming a repeat with repeatOf includes them too. A launch on which everyone would be skipped is refused (400) and nothing is created; an add of contacts on which everyone is skipped adds no one, with no error. A launch, or an add of contacts, is refused with 409 duplicate_campaign when it would leave a campaigninsteadwith exactly the same set ofrelaunching.contactResendaddresses and the sameidempotencyKeysubjects and bodies in the same step order as another campaign of this account, however long ago that one was launched and whether it is running, paused, ended or finished, so the same people are not sent the same emails twice. Nothing else is compared: a different name, offer, time zone, send window, delay, date, stopOnReply, listSource, row order or contact name, company or vars is still the same campaign. The refusal creates nothing and carries existingCampaignId, existingCampaignName and existingCampaignState. To repeat that campaign on purpose, send the request again with repeatOf set to that existingCampaignId: it is accepted once, and only while it names the most recent such campaign. To retry a call whose response you lost:,thatresend it with the same idempotency key, which replays the original resultinsteadinstead.ofAbeingcampaignrefused.launchedCampaignsonthatadiffertrial account emailed nobody and counts only while the account is still on the trial; the built-in demo campaign never counts. A trial account can launch up to 5 test campaigns in anyfield24otherhours,thaneachlistSourceholding(up to 100 contacts, which isnotenoughpartfor a small test. Both are limits ofthatthecheck)trial,andnotdeliberateofrelaunchesColdrig:aftergoingthoselive60removessecondsthem. Nothing in a campaign launched on the trial is ever really sent: when the account goes live, the emails it still has waiting arenevercanceledblocked.and it cannot be resumed or added to, so launch a new campaign to send real email.
⟨187 unchanged words⟩ "minLength":1,"type":"string"},"includeAlreadyEmailed":{"description":"Optional. 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.","type":"boolean"},"leads":{"items":{"properties":{"company ⟨25 unchanged words⟩ ,"type":"string"},"firstName":{"default":"","maxLength":200,"minLengthtype":1"string"},"vars":{"additionalProperties":{"maxLength":2000,"type":"string"},"description":"Optional per-lead values for your own {{tokens}}: a flat map of word-keys (letters, digits, underscore; up to 64 chars) to one-line strings (up to 2000 chars), at most 50 keys. A {{key}} used in any subject or body must be present, non-empty, on EVERY lead or the launch is refused (400) naming it. A key may not be firstName or company."required,"propertyNames":[{"firstNamepattern
⟨8 unchanged words⟩ counts{sent,reply,bounce,complaint,unsubscribe,failed,soft_bounce,auto_reply} }], newest first — no per-campaign ⟨12 unchanged words⟩
⟨21 unchanged words⟩ notes, tags, suppressed, lastEventType, lastEventTs,createdAt.createdAt, threadId (that lead's conversation: pass it to thread for the lead's timeline; null if nothing was scheduled). Filters: campaign, interestStatus, suppressed, replied. This ⟨31 unchanged words⟩
⟨7 unchanged words⟩ reply, bounce, complaint, unsubscribe, failed, soft_bounce, auto_reply } — the sameshapeevent counts as campaign_results but summed tenant-wide (no per-contact fields) (bounce = hard only, opens nottrackedtracked; auto_reply = automatic replies such as out-of-office, never counted in reply). Use campaign_results for one campaign, list_campaigns ⟨5 unchanged words⟩
Pause ONE campaign: its status → 'paused',soandthenothingtickmoreschedulesinnoitfurthersends; its remaining steps are HELD, not cancelled (already-sent mail isunaffected; thereunaffected)is—nocall resumetool).to continue it. Input: campaignId. Returns { paused: true }. ⟨19 unchanged words⟩ already gave): their remaining steps are cancelled for good (resume does not bring them back), the campaign keeps running for everyone else, ⟨42 unchanged words⟩
⟨7 unchanged words⟩ at once (each active status → 'paused';thenothingtickmoretheninschedulesthemnosends,furtherandsendstheir remaining steps are held). No inputs. Returns { pausedAll: true }. Use pause to pause a single campaign byid.id, and resume (one call per campaign) to continue one.
⟨21 unchanged words⟩ messages[] }, each message { type (sent/reply/bounce/...sent/reply/auto_reply/bounce/...; auto_reply = an automatic reply such as out-of-office), step (the sequence step of a sent message, null otherwise), ts, messageId, metadata }, oldest ⟨15 unchanged words⟩
Add contacts to a running campaign. Inputs: campaignId, leads[] (same shape as launch_campaign: email, optional firstName, company, vars), listSource (REQUIRED on a crowdfunding account). Only an active or paused campaign of this account (404 if unknown; 400 if past its end date). Each new contact starts at step 1 on the campaign's own schedule, under every launch rule: a {{firstName}} or {{key}} the sequence uses must have a value on every new contact, or the call is refused (400) and nothing is added. Skipped and counted, never an error: an address already in this campaign (unchanged), an a…
Resume ONE paused campaign: its status → 'active'. Input: campaignId. Returns { resumed: true }. Only a paused campaign of this account can be resumed: 404 if unknown, refused (400) if it is not paused, is past its end date, or is a trial campaign on an account that has gone live. On a paid account, resuming sends nothing by itself: the held steps go out through the normal schedule, inside the send window and the daily caps. A step whose time passed while paused goes out at the next allowed slot, one step at a time per lead: the step after it still waits its own delayDays from when that one a…
⟨21 unchanged words⟩ mailbox, campaign, label, read, includeNonreply (bounces/OOOthreads whose last event is a bounce, complaint or automatic reply such as out-of-office; default true), archived (exclude|include|only). Use ⟨5 unchanged words⟩
⟨151 unchanged words⟩ and {{company}} (filled from eachleadlead; firstName and company are both optional) plus your own {{key}} for any key every lead carries in its optional `vars` map (word-keys to one-line strings, e.g. vars:{ icebreaker:"..." }; a key may not be firstName or company). A value is inserted once as plain text. A launch is refused (400), naming the token and how many leads lack it, ifanya subject or body uses {{firstName}} or a {{key}} that anyotherlead has no non-empty value for — no fallback word is invented; it is also refused if any subject or body uses a {{token…}} that is not one word, a single-brace form of a known token ⟨314 unchanged words⟩ whose previous step never went out is skipped. Lead addresses are trimmed and lower-cased, and an address repeated in leads[] is kept once (its first row). Suppressed leads are skipped. Returns { campaignId, sendWindow, timezone, contacts, suppressed, duplicatesRemoved, alreadyEmailed, endByShortfall, warning, nextSteps } — sendWindow and timezone are what actuallyapplied.applied;Campaignscontacts is how many leads were stored, suppressed how many of those are on your suppression list (never scheduled), duplicatesRemoved the repeated rows dropped, alreadyEmailed the leads left out because they already got this email. With an endBy, endByShortfall is at least how many of the contacts this campaign will email cannot get their first email before endBy on this account's current mailboxes (a guaranteed minimum: each mailbox's daily cap on each day, warmup and throttle included, on the sendWindow's days and hours in timezone, and on a paid account at most one email per recipient domain in each 5-minute sendrealround,identicalchecktorunsone(a launch that would be refused is refused the same way), nothing is created or sent, no idempotencyKey is recorded, and it returns { preview, sendWindow, timezone, contacts, suppressed, duplicatesRemoved, alreadyEmailed, endByShortfall, warning, samples } — the same endByShortfall and warning the launch would carry; samples[] is { email, steps[] } for the first three leads, each step its rendered subject and body (every real send then appends the required footer: sender identity, postal address, unsubscribe link). People who already got this email are skipped: a contact is left out, never stored or emailed, when another campaign of this accountmadehas sent them, or still has waiting for them, an email with the same subject and body as this campaign's first email, as written before {{tokens}} are filled in (spacing differences do not count). A trial's test email counts only while thelastaccount60issecondsstill on the trial, and the built-in demo campaign never counts. alreadyEmailed in the answer isREFUSEDhowwithmany409were{skippedcode(anyone also on your suppression list is counted there, not in suppressed):'duplicate_campaign'tell the person in plain words,existingCampaignIdfor}examplerather"skippedthan12contactingpeople who already got this email". To include them, send includeAlreadyEmailed: true on the launch itself (previewOnly: true shows the count first), or after a launch add them to that sameprospectscampaigntwicewith—add_leadscheckand includeAlreadyEmailed: true. Never launch the same list again to include them: that emails everyone who was not skipped a second time. Naming a repeat with repeatOf includes them too. A launch on which everyone would be skipped is refused (400) and nothing is created; an add of contacts on which everyone is skipped adds no one, with no error. A launch, or an add of contacts, is refused with 409 duplicate_campaign when it would leave a campaigninsteadwith exactly the same set ofrelaunching.contactResendaddresses and the sameidempotencyKeysubjects and bodies in the same step order as another campaign of this account, however long ago that one was launched and whether it is running, paused, ended or finished, so the same people are not sent the same emails twice. Nothing else is compared: a different name, offer, time zone, send window, delay, date, stopOnReply, listSource, row order or contact name, company or vars is still the same campaign. The refusal creates nothing and carries existingCampaignId, existingCampaignName and existingCampaignState. To repeat that campaign on purpose, send the request again with repeatOf set to that existingCampaignId: it is accepted once, and only while it names the most recent such campaign. To retry a call whose response you lost:,thatresend it with the same idempotency key, which replays the original resultinsteadinstead.ofAbeingcampaignrefused.launchedCampaignsonthatadiffertrial account emailed nobody and counts only while the account is still on the trial; the built-in demo campaign never counts. A trial account can launch up to 5 test campaigns in anyfield24otherhours,thaneachlistSourceholding(up to 100 contacts, which isnotenoughpartfor a small test. Both are limits ofthatthecheck)trial,andnotdeliberateofrelaunchesColdrig:aftergoingthoselive60removessecondsthem. Nothing in a campaign launched on the trial is ever really sent: when the account goes live, the emails it still has waiting arenevercanceledblocked.and it cannot be resumed or added to, so launch a new campaign to send real email.
⟨187 unchanged words⟩ "minLength":1,"type":"string"},"includeAlreadyEmailed":{"description":"Optional. 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.","type":"boolean"},"leads":{"items":{"properties":{"company ⟨25 unchanged words⟩ ,"type":"string"},"firstName":{"default":"","maxLength":200,"minLengthtype":1"string"},"vars":{"additionalProperties":{"maxLength":2000,"type":"string"},"description":"Optional per-lead values for your own {{tokens}}: a flat map of word-keys (letters, digits, underscore; up to 64 chars) to one-line strings (up to 2000 chars), at most 50 keys. A {{key}} used in any subject or body must be present, non-empty, on EVERY lead or the launch is refused (400) naming it. A key may not be firstName or company."required,"propertyNames":[{"firstNamepattern
⟨8 unchanged words⟩ counts{sent,reply,bounce,complaint,unsubscribe,failed,soft_bounce,auto_reply} }], newest first — no per-campaign ⟨12 unchanged words⟩
⟨21 unchanged words⟩ notes, tags, suppressed, lastEventType, lastEventTs,createdAt.createdAt, threadId (that lead's conversation: pass it to thread for the lead's timeline; null if nothing was scheduled). Filters: campaign, interestStatus, suppressed, replied. This ⟨31 unchanged words⟩
⟨7 unchanged words⟩ reply, bounce, complaint, unsubscribe, failed, soft_bounce, auto_reply } — the sameshapeevent counts as campaign_results but summed tenant-wide (no per-contact fields) (bounce = hard only, opens nottrackedtracked; auto_reply = automatic replies such as out-of-office, never counted in reply). Use campaign_results for one campaign, list_campaigns ⟨5 unchanged words⟩
Pause ONE campaign: its status → 'paused',soandthenothingtickmoreschedulesinnoitfurthersends; its remaining steps are HELD, not cancelled (already-sent mail isunaffected; thereunaffected)is—nocall resumetool).to continue it. Input: campaignId. Returns { paused: true }. ⟨19 unchanged words⟩ already gave): their remaining steps are cancelled for good (resume does not bring them back), the campaign keeps running for everyone else, ⟨42 unchanged words⟩
⟨7 unchanged words⟩ at once (each active status → 'paused';thenothingtickmoretheninschedulesthemnosends,furtherandsendstheir remaining steps are held). No inputs. Returns { pausedAll: true }. Use pause to pause a single campaign byid.id, and resume (one call per campaign) to continue one.
⟨21 unchanged words⟩ messages[] }, each message { type (sent/reply/bounce/...sent/reply/auto_reply/bounce/...; auto_reply = an automatic reply such as out-of-office), step (the sequence step of a sent message, null otherwise), ts, messageId, metadata }, oldest ⟨15 unchanged words⟩
Add contacts to a running campaign. Inputs: campaignId, leads[] (same shape as launch_campaign: email, optional firstName, company, vars), listSource (REQUIRED on a crowdfunding account). Only an active or paused campaign of this account (404 if unknown; 400 if past its end date). Each new contact starts at step 1 on the campaign's own schedule, under every launch rule: a {{firstName}} or {{key}} the sequence uses must have a value on every new contact, or the call is refused (400) and nothing is added. Skipped and counted, never an error: an address already in this campaign (unchanged), an a…
Resume ONE paused campaign: its status → 'active'. Input: campaignId. Returns { resumed: true }. Only a paused campaign of this account can be resumed: 404 if unknown, refused (400) if it is not paused, is past its end date, or is a trial campaign on an account that has gone live. On a paid account, resuming sends nothing by itself: the held steps go out through the normal schedule, inside the send window and the daily caps. A step whose time passed while paused goes out at the next allowed slot, one step at a time per lead: the step after it still waits its own delayDays from when that one a…
{"$schema":"http://json-schema.org/draft-07/schema#","properties":{"endBy":{"description":"Optional fixed end date, as an ISO 8601 instant with a zone. From then on nothing more in this campaign sends: every step still waiting is skipped and recorded as a 'failed' event with reason campaign_ended. Paid accounts only: a trial account that sends one is refused 400. Must be later than now and later than every step's notBefore. Omit to let the sequence run to its end.","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$","type":"string"},"idempotencyKey":{"description":"Optional idempotency key: ⟨71 unchanged words⟩ "minItems":1,"type":"array"},"listSource":{"description":"Where this lead list came from, in a sentence (e.g. \"our own past-donor CRM export\"). REQUIRED on a crowdfunding account: a launch with no source, or \"unknown\", is refused (400) — cold lists are allowed only with a stated source. Optional elsewhere. Max 500 characters.","maxLength":500,"type":"string"},"name":{"maxLength":200,"minLength":1, ⟨204 unchanged words⟩ ":60,"minimum":0,"type":"integer"},"notBefore":{"description":"Optional fixed date this step is held until, as an ISO 8601 instant with a zone (e.g. \"2026-11-03T09:00:00-05:00\" or \"...Z\"). The step goes out at the later of notBefore and delayDays after the previous step actually sent, still inside sendWindow. Paid accounts only: a trial account that sends one is refused 400, so use delayDays while on trial. Omit to send on delayDays alone.","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$","type":"string"},"step":{"maximum":9007199254740991,"minimum ⟨140 unchanged words⟩
⟨27 unchanged words⟩ paused: true }. 404 if not found. Add leadEmails to stop ONLY those leads in this campaign instead (e.g. a donor who already gave): their remaining steps are cancelled, the campaign keeps running for everyone else, and nobody is suppressed — returns { paused: false }. Matching ignores capitalisation; if any address is not a lead of this campaign, nothing is stopped and the call is refused (404) naming them. Use pause_all to pause every active campaign at once.
⟨12 unchanged words⟩ ","minLength":1,"type":"string"},"leadEmails":{"description":"Optional. Stop ONLY these leads in this campaign (any capitalisation): their remaining steps are cancelled, the campaign keeps running for everyone else, and nobody is added to the suppression list. Every address must be a lead of this campaign, or nothing is stopped and the call is refused naming the ones that are not. Omit to pause the whole campaign.","items":{"format":"email","pattern":"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$","type":"string"},"maxItems":5000,"minItems":1,"type":"array"}},"required":["campaignId"],"type":"object"}
Plan a dated outreach campaign (for example a fundraising drive) and get its price. Read-only: nothing is booked, charged or changed. Inputs: contacts, campaignStart (first campaign day, YYYY-MM-DD, UTC), campaignDays, bookingDate (the day you would book, normally today), and optionally domainsAlreadyHeld (default 0; infrastructure_status shows `domains`) and listIsOwnOptIn (your own opted-in donors; default false). Returns { mailboxes, domainsToBuy, bookBy, advanceStartOn, safeReach, priceCents, warnings[] }. safeReach is how many contacts the warm-up ramp can safely reach if you book on boo…
Send private feedback to the Coldrig founders about how the product is working for you. Inputs: score (integer 0-10, optional) and/or text (1-2000 chars, optional); at least one is required. It is stored privately and emailed to the founders, never published, and it opens no support ticket and changes nothing on your account (use contact_operator for a problem that needs an answer). Each call is a separate submission. Returns { feedbackId, received }.
{"$schema":"http://json-schema.org/draft-07/schema#","properties":{"endBy":{"description":"Optional fixed end date, as an ISO 8601 instant with a zone. From then on nothing more in this campaign sends: every step still waiting is skipped and recorded as a 'failed' event with reason campaign_ended. Paid accounts only: a trial account that sends one is refused 400. Must be later than now and later than every step's notBefore. Omit to let the sequence run to its end.","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$","type":"string"},"idempotencyKey":{"description":"Optional idempotency key: ⟨71 unchanged words⟩ "minItems":1,"type":"array"},"listSource":{"description":"Where this lead list came from, in a sentence (e.g. \"our own past-donor CRM export\"). REQUIRED on a crowdfunding account: a launch with no source, or \"unknown\", is refused (400) — cold lists are allowed only with a stated source. Optional elsewhere. Max 500 characters.","maxLength":500,"type":"string"},"name":{"maxLength":200,"minLength":1, ⟨204 unchanged words⟩ ":60,"minimum":0,"type":"integer"},"notBefore":{"description":"Optional fixed date this step is held until, as an ISO 8601 instant with a zone (e.g. \"2026-11-03T09:00:00-05:00\" or \"...Z\"). The step goes out at the later of notBefore and delayDays after the previous step actually sent, still inside sendWindow. Paid accounts only: a trial account that sends one is refused 400, so use delayDays while on trial. Omit to send on delayDays alone.","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$","type":"string"},"step":{"maximum":9007199254740991,"minimum ⟨140 unchanged words⟩
⟨27 unchanged words⟩ paused: true }. 404 if not found. Add leadEmails to stop ONLY those leads in this campaign instead (e.g. a donor who already gave): their remaining steps are cancelled, the campaign keeps running for everyone else, and nobody is suppressed — returns { paused: false }. Matching ignores capitalisation; if any address is not a lead of this campaign, nothing is stopped and the call is refused (404) naming them. Use pause_all to pause every active campaign at once.
⟨12 unchanged words⟩ ","minLength":1,"type":"string"},"leadEmails":{"description":"Optional. Stop ONLY these leads in this campaign (any capitalisation): their remaining steps are cancelled, the campaign keeps running for everyone else, and nobody is added to the suppression list. Every address must be a lead of this campaign, or nothing is stopped and the call is refused naming the ones that are not. Omit to pause the whole campaign.","items":{"format":"email","pattern":"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$","type":"string"},"maxItems":5000,"minItems":1,"type":"array"}},"required":["campaignId"],"type":"object"}
Plan a dated outreach campaign (for example a fundraising drive) and get its price. Read-only: nothing is booked, charged or changed. Inputs: contacts, campaignStart (first campaign day, YYYY-MM-DD, UTC), campaignDays, bookingDate (the day you would book, normally today), and optionally domainsAlreadyHeld (default 0; infrastructure_status shows `domains`) and listIsOwnOptIn (your own opted-in donors; default false). Returns { mailboxes, domainsToBuy, bookBy, advanceStartOn, safeReach, priceCents, warnings[] }. safeReach is how many contacts the warm-up ramp can safely reach if you book on boo…
Send private feedback to the Coldrig founders about how the product is working for you. Inputs: score (integer 0-10, optional) and/or text (1-2000 chars, optional); at least one is required. It is stored privately and emailed to the founders, never published, and it opens no support ticket and changes nothing on your account (use contact_operator for a problem that needs an answer). Each call is a separate submission. Returns { feedbackId, received }.
Reach a human operator—for anything list_messages/infrastructure_statuscan'tcannot answer (a stuck vendor issue, a billing ⟨5 unchanged words⟩ Inputs: body (1-2000 chars), urgency ('normal' | 'needs_human'normal|needs_human, default'normal'normal). Files asupportticketand, notifies the operator; returns { ticketId, note, deduplicated }. Works in every account state atenanttokenstillauthenticates in,including(dunning-suspended, cancelingand, canceled) —thisadmin-terminated isexactlythechannel for 'why is my account suspended?'. The ONEone exceptionis an admin-TERMINATED (abuse) account,whose token isrejected atauth with 401 before this tool runs.auth. Theoperator'sreply arrives as a message on THIS account (polllist_messages / infrastructure_status.messages[]list_messages/infrastructure_status —there isnoseparatereply-fetch call). Sending the IDENTICALbody AND urgencybody+urgency again within an hour returns the SAME ticketIdand does not file a second ticket or send,ano secondalertticket/alert (`deduplicated: true`on that response; `false` when a new ticket was actually filed)— no separate idempotency key is needed to retry a dropped response. This is a. TEXTmatchMATCH,notNOTanANintentINTENTmatchMATCH:the platform cannot telladeliberate retry from a coincidentally-identical NEW message, so agenuinely newaskmessage withthe sameidentical wording collapsesintotheearlier ticket justsameasway, silently — vary the wording (or raise urgency,which isalwaystreated as an escalation and filesa new ticket)if you have something new to say. 'needs_human'.also`needs_human` bypasses the~10-minute~10-min ops-emailthrottle so an urgent message is pushed immediately.throttle. Rate-limited to5 calls/hour per tenant5/hour/tenant — a 429 names retryAfter (seconds)when hit..
Warmup + provisioning progress per mailbox.New mailboxes are ramp-limited server-side: 5 sends/day week 1 rising to 40/day after 4 weeks; current dailyCap for each mailbox is in the response below — ramp caps permit CAPPED sending from day 1, never zero until fully warmed.Returns { domains, mailboxes, sendReady, mailboxHealth[], messages[], nextSteps }; each mailbox: warmupDay, dailyCap, sentToday, sendReady, delivStatus (healthy/throttled/paused),.complaint/bounce/softBounceNewratesmailboxes(first-partyramp-limitmeasured),server-side:vendorReputationScore5+sends/dayvendorPlacementRateweek(VENDOR-REPORTED1,not40/dayfirst-partyweekmeasurements4 —the control loop uses local signals only; BOTH ARE null whenever the provider does not reportdailyCap/sentTodaythem,showwhichrealiscappedthesendingnormalfromcasedaytoday1,so treat null as 'not measured' andneveras zero), lastPolledAt.zero. Per-mailbox `sendReady` is a FULLY-RAMPED flag, NOT a send gate —a mailbox below fullbelow-ramprampmailboxes stillsendssend, capped atitstheir own`dailyCap`; `sendReady` only says warmup has finished, sodailyCap; read`dailyCap`/`sentToday`dailyCap/sentToday foractual send capacity, never `sendReady` alone. Thecapacity.top-levelTop-level `sendReady` is the AND across ALL mailboxes (trueonlyonce every onehas finishedisrampingramped) — forwhether THISonemailboxmailbox'scancapacitysenduserightitsnow,owntheflag.per-mailboxEachflagmailboxnextcarriestodelivStatusit(healthy/throttled/paused),iscomplaint/bounce/softBouncetherates;onevendorReputationScore/vendorPlacementRatethataremattersVENDOR-REPORTED,not thenulltop-levelwhenone.unmeasured. Vendor-pool warmup(the underlying reputation-building the provider runs)is FEED-INVISIBLE bydesign: nothing in this response surfaces the vendor's own warmup internals — dailyCap/warmupDay/sendReady here are this platform's own ramp schedule, computed independently of whatever the vendor is doing in its pool, so do not expect a vendor-side warmup event to show up here.design. `messages[]`surfaces system notices this account should act on (e.g. a setup step that needs a retry, a mailbox credential that just went live) — each has kind, severity ('info' = resolves on its own | 'action_required' = nothing progresses until you act, and acting works | 'operator_pending' = the platform has stopped and nothing you change will restart it, but an operator can clear the blocker and then retrying the SAME call with the SAME idempotency key completes it — keep your inputs as they are and do not give up | 'terminal' = the platform has STOPPED, retrying will never help and only a human can move it — the actionHint names contact_operator), body, actionHint (structured — e.g. which tool + idempotencyKey to retry with), createdAt; poll this alongside the mailbox fields — capped at the newestpreviews 5(operator replies sort first, so system churn cannot evict one, but a 6th DISTINCTunackedmessage of either kind can still fall off this preview;noticesuse(severities: list_messagesfor the complete, paginated history). Reading them here does NOT mark them read — only an explicit ack_message call sets readAt. Unacked operator messages sort first here, then system notices newest-first, so a human reply;isreading neverpushed out of the 5 by system churn; list_messages is the full paginatedmarkssurface.read. `nextSteps` nameswhat this account should dothe next—action (seesetup_infrastructure's description for the discriminated shape.setup_infrastructure). Use account/metrics foraccount-widerollups.
⟨18 unchanged words⟩ ), cursor-paginated. Unacked messages sort first (newestfirstwithin that group), then ackedones(also newest first). Returns { messages[], nextCursor }; eachmessage: id, kind, severity ('info'|'action_required'|'operator_pending'|'terminal' —'info''operator_pending'=meansresolvestheonplatformitshasownstopped,|nothing'action_required'you=changeactrestartsand it,andprogressesan|operator'operator_pending'clearing=theplatformblockerstopped,makesonlytheanSAMEoperatorretrycanwork,restartsoit,keepretrying thesameSAMEinputscallandthenidempotencyworkskey;| 'terminal'means the= platformhasstoppedand, only a human can move it,sodo NOT retry), body, actionHint(structured — e.g. which tool + idempotencyKey to retry with), source ('system'|'operator'system|operator), createdAt, readAt. `readAt` is set ONLY byan explicitack_messagecall—LISTING messageslisting never marksthem, here or anywheremessageselseread, socalling this tool leaves every readAt exactly as it was. Aa null readAttherefore means 'not acked', which is NOT evidence thedoesmessagenotwasmean never seen: it may have been read and,simplyonly not yet acknowledged. Use ack_message toacknowledgestop oneby id so it stopsresurfacing. infrastructure_statusalsoinlines the newest 5 unackedmessages for a quick glance —messages; this is the full paginated surface.
Downgrade: release your N NEWEST live mailboxesnow and,lowerlowering the billed quantity. Inputs: count, acknowledged (must be true —this is a quoted, irreversible-this-cycle consent: therelease is immediatefor provisioningbutthere isirreversible;NOno mid-cyclecredit; thecredit, lower pricetakes effectstarts next renewal, minimum 5mailboxes / $99mailboxes/$99). Returns { releasedCount, failedCount, unreleased, billing, deduplicated }where billing is the new projected monthly. `releasedCount` is what COMPLETED and can be less than the `count` you asked for; `failedCount` is how many the provider refused, which are STILL LIVE and still billed, and `unreleased` names those addresses — read them before concluding a downgrade landed. `deduplicated: true` means this call did NOT re-perform the downgrade — every count above describes an EARLIER call's already-recorded outcome under the same idempotencyKey, not new work done just now. This can be true even after the 30-day idempotency window itself has expired: the underlying release record persists independently, so a reused key still reports no re-release rather than silently starting a fresh one.. `count` is RELATIVE— it releases(that many MOREmailboxes,it doesnotset youra fleetto that size — and release cannot be undone through this API, sotarget). ALWAYS pass an idempotencyKey: the FIRST call under a keyresolvesfixes WHICH mailboxes torelease and records that exact set, sorelease;everya later callcarryingwith the SAME key can onlyeverfinish thatsameset.A same-key retry re-attempts the stragglers and`releasedCount` canneverbereleaselessathanmailbox`count`outside—them,`failedCount`whatevernames`count`howyoumanysendthewithvendoritrefused (the recorded setstillwinslive,overbilled;a`unreleased`changedlistscountthem);.aResendretrywithWITHOUTtheasame keyalways releases another `count`.untilA`failedCount`genuineisSECOND0.downgrade`deduplicated:istrue`therefore=aNONEWnewkeywork —reusinganold one can onlyearliereverrecordedfinishoutcomewhatunder that keyalready askedwasfor.replayed. Acall that came back with failedCount above zero did NOT finish, so its key is not frozen: resend the identical request with the same key until failedCount is 0 — from then on that key replays the finished result and releasesgenuinelynothingNEWfurther.downgradeIfneeds acallNEWreturnskey. 409 = a releaseisalreadyrunning for this account: wait, thenrunning;re-readre-check infrastructure_status beforedeciding whether to retry.retrying. To ADD mailboxes use setup_infrastructure/ configure_byo_domainor(request_managed_mailboxes).configure_byo_domain.
⟨17 unchanged words⟩ messageId, deduplicated }. A reply is realsendingsend volumeand is, governedexactlylike campaign sends:itcounts againstthatthemailbox'smailbox daily cap(sentToday +1,visible in infrastructure_status),anditis REFUSED—(never silently dropped—) when the recipient is suppressed, the mailbox is deliverability-paused, or the cap is usedup.upA— a refusal returns { error, code:'send_blocked'send_blocked, reason:'suppressed'|'mailbox_paused'|'daily_cap_reached'suppressed|mailbox_paused|daily_cap_reached, retryable }:; retryable (cap) clearsat thenext daily rollover, non-retryable does not,so— stop retrying,anddodon'tnot loop replies to manufacture volume. Idempotent: identical ⟨8 unchanged words⟩ stable idempotencyKey (else a body hash is used, matched only 10 minutes) so a dropped-response retrycan'tcannot double-send. `deduplicated: true`means= NO new emailwas sent —sent; `messageId` is from an earliersend this call matched. `deduplicated: false` means this platform sent the call onward as a NEW send, which is normally a new email — but it is not a guarantee one left the building: with NO idempotencyKey the body hash is only matched here for 10 minutes, while the sending provider keeps its own record of an identical body on the same thread for longer, and can collapse a much later repeat into the originalmatching send.So onPass along-delayed identical repeat you may get `deduplicated: false` with the ORIGINAL send's messageId and no new email. PASS AN IDEMPOTENCYKEYkey, or vary the body, ifyou needa repeattomust genuinely go out. 404 if no sending mailbox ⟨6 unchanged words⟩
Provision sendinginfrastructureinfra: buybrandedlookalike domains, create mailboxes, start warmup.New mailboxes are ramp-limited server-side — 5 sends/day in week 1, rising to 40/day after 4 weeks — and your own calls cannot exceed that cap; poll infrastructure_status for the current dailyCap.Inputs: brand, primaryDomain, domains + inboxesEachcounts(or distribution), persona, physicalAddress, senderIdentity. Billing isper-provisioned-mailbox ($49 platform +permailbox, graduated: 1-5 at $10, 6-20 at $8, 21+ at $7; 5-mailbox minimum) and the billed quantity follows what you provision here —provisionedpassmailbox; quoteOnly:truefirst to preview the new count + projected monthly price before committing (no silent capacity addition). Every response carries a `billing` projection { provisionedAfter (the live count AFTER this call — reality, not the ask), projectedMonthlyCents, formula }: on quoteOnly it's the preview, on an actual provision it's the real post-provision bill (a capacity-limited partial reflects only what landed). Returns { jobId, billing } — `jobId` is a correlation id only, not a trackable job handle: there is no job store and no endpoint that accepts it, so do not poll for it or treat its presence as meaningful; it exists to correlate this response with logs. A `provisioning` field is present ONLY when the call returned still OWING work, and names which state: 'pending' (a domain's DNS registration is still completing — `pendingDomain` names one of them) or 'capacity_pending' (held at a spend limit, so polling will NOT progress until an operator raises it — call contact_operator instead of waiting). Its ABSENCE is what says the provision finished; the only way to find out is to re-call setup_infrastructure or read infrastructure_status's `nextSteps`. An outcome carrying `provisioning`, and a quoteOnly preview, are deliberately NOT recorded against your idempotencyKey — retrying with the same key re-runs the call and finishes the job rather than replaying the unfinished answer.previews`domains`costandfirst.`inboxesEach``domains`/`inboxesEach` are theinfrastructureinfra you want to HAVE, not an amount to add:each call keeps and resumes what this account already has and buys only the shortfall, so to provision MORE you ask for a LARGER number (domains:2 after a call that provisioned one buys the second; raising inboxesEach tops each domain up). Repeating a call therefore never buys twice, whatever you do with idempotencyKey—resend it, change it, or omit it. The key controls response replay only and has no bearing on what is purchased, soaretry is always safe. A domain this account already registered but that never landed in your account (a prior call that failed after the purchase) is ADOPTED on the retry at zero extra cost rather than bought again. A domain whose DNS setup has not finished yet is recorded and never lost — there is no per-domain DNS field to poll (infrastructure_status reports a domain count, not per-domain detail), so read its `nextSteps` for what to do next, andrepeatthis samecallto converge on the domain and finish its DNS setup. Slot semantics: `domains: N` covers ordinalsnever0..N-1,buysandtwice; each ordinal 0..domains-1 fills to its ownmailbox count (uniform via `inboxesEach`, or per-ordinal via `distribution` — see below); a repeat call at the SAME `domains` provisions nothing new once every ordinal is satisfied, so reaching ordinal 1 needs `domains:2`, not a second `domains:1` call.count. Mailbox addresses are DETERMINISTIC— derivedfrom`persona` + ordinal + slotpersona+ordinal+slot —sokeep`persona` EXACTLY as it was on a resume or retry; changing it mid-account does not move existing addresses but does change what a NEW ordinal derives. `distribution` (optional, alternative to `inboxesEach`) names the per-ordinal mailbox count directly — one entry per domain ordinal, e.g. `[3, 2]` for 3 mailboxes on domain 0 andpersona2unchanged ondomain 1 — forashape `inboxesEach`'s single uniform number cannot express; supply exactly one of the two (an array with `domains` entries, or the uniform count).retry.`registerDomains``registerDomains:true` isthis tenant's opt-inrequired consentto real domain purchases made on the platform's own account (our COGS — your bill is unchanged, mailbox-count-based only); only the operator's own global switch being armed too can ever actually enable it. OMITTING it leavesbefore anyconsent you previously gave UNCHANGED — pass `false` explicitly to revoke it. A call that omitsNEW`registerDomains`domain(orpurchase;sendsomitting it`false`)on abuy-bearing request (one that needs to purchase a newbuydomain)call is refusedBEFORE any spend. Once this account has consented at least once, that refusal is a400`registrar_optin_missing`: 'registerDomains was not set on this request' — resend the identical call withregistrar_optin_missingit(self-correctsetbytoresending trueto self-correct; this is, never an operatorescalation. (An account that has never consented, on a platform where domain registration is not yet enabled at all, instead sees the operator-facing 503 `registrar_unarmed` — indistinguishable from the outside except by status code, but the fix is the same: set `registerDomains: true` and retry.) Separately, a `registerDomains:true` call with NO `registrant` anywhere (neither in this request nor persisted from a prior oneescalation)fails with a 400 `incomplete_registrant` naming the missing fields— alsobefore any purchase. Fresh candidates are de-duplicated against what this account already owns and checked for availability; if a callneedsto buy a domain and no available un-owned candidate exists it fails with a 400 naming that, never a silent repeat purchase (a call that needs to buy nothing is unaffected).`registrant`(a full registrant-of-record object { firstName,lastName, email, phone, addressLine1, city, state, country, postalCode, organization (optional, defaults to brand) }) is OPTIONAL even when `registerDomains` is true whenever a complete registrant isunless already onfile from a prior call — supplying one makes it authoritative for THIS call; omitting it re-uses the persisted one. This platform never invents a domain registrant's legal identity, so a call with no registrant anywhere (neither supplied nor persisted) is rejected naming the missing fields, at or before the actual purchase — never silently. There isfile. NO background retry exists: acall`provisioning`returningresult`provisioning`(pending|capacity_pending) needsthe callerYOU to retryit; the platform does not complete it for you on a timer. Every response carries `nextSteps` (see the discriminatedto`status`/`steps`progress;shape)capacity_pending—needsthecontact_operatoraccount'sinstead.ownReturnsnext{actionjobId,computed fresh each timebilling,so a stalled setup isprovisioning?,nevernextStepssilent.}.
Reach a human operator—for anything list_messages/infrastructure_statuscan'tcannot answer (a stuck vendor issue, a billing ⟨5 unchanged words⟩ Inputs: body (1-2000 chars), urgency ('normal' | 'needs_human'normal|needs_human, default'normal'normal). Files asupportticketand, notifies the operator; returns { ticketId, note, deduplicated }. Works in every account state atenanttokenstillauthenticates in,including(dunning-suspended, cancelingand, canceled) —thisadmin-terminated isexactlythechannel for 'why is my account suspended?'. The ONEone exceptionis an admin-TERMINATED (abuse) account,whose token isrejected atauth with 401 before this tool runs.auth. Theoperator'sreply arrives as a message on THIS account (polllist_messages / infrastructure_status.messages[]list_messages/infrastructure_status —there isnoseparatereply-fetch call). Sending the IDENTICALbody AND urgencybody+urgency again within an hour returns the SAME ticketIdand does not file a second ticket or send,ano secondalertticket/alert (`deduplicated: true`on that response; `false` when a new ticket was actually filed)— no separate idempotency key is needed to retry a dropped response. This is a. TEXTmatchMATCH,notNOTanANintentINTENTmatchMATCH:the platform cannot telladeliberate retry from a coincidentally-identical NEW message, so agenuinely newaskmessage withthe sameidentical wording collapsesintotheearlier ticket justsameasway, silently — vary the wording (or raise urgency,which isalwaystreated as an escalation and filesa new ticket)if you have something new to say. 'needs_human'.also`needs_human` bypasses the~10-minute~10-min ops-emailthrottle so an urgent message is pushed immediately.throttle. Rate-limited to5 calls/hour per tenant5/hour/tenant — a 429 names retryAfter (seconds)when hit..
Warmup + provisioning progress per mailbox.New mailboxes are ramp-limited server-side: 5 sends/day week 1 rising to 40/day after 4 weeks; current dailyCap for each mailbox is in the response below — ramp caps permit CAPPED sending from day 1, never zero until fully warmed.Returns { domains, mailboxes, sendReady, mailboxHealth[], messages[], nextSteps }; each mailbox: warmupDay, dailyCap, sentToday, sendReady, delivStatus (healthy/throttled/paused),.complaint/bounce/softBounceNewratesmailboxes(first-partyramp-limitmeasured),server-side:vendorReputationScore5+sends/dayvendorPlacementRateweek(VENDOR-REPORTED1,not40/dayfirst-partyweekmeasurements4 —the control loop uses local signals only; BOTH ARE null whenever the provider does not reportdailyCap/sentTodaythem,showwhichrealiscappedthesendingnormalfromcasedaytoday1,so treat null as 'not measured' andneveras zero), lastPolledAt.zero. Per-mailbox `sendReady` is a FULLY-RAMPED flag, NOT a send gate —a mailbox below fullbelow-ramprampmailboxes stillsendssend, capped atitstheir own`dailyCap`; `sendReady` only says warmup has finished, sodailyCap; read`dailyCap`/`sentToday`dailyCap/sentToday foractual send capacity, never `sendReady` alone. Thecapacity.top-levelTop-level `sendReady` is the AND across ALL mailboxes (trueonlyonce every onehas finishedisrampingramped) — forwhether THISonemailboxmailbox'scancapacitysenduserightitsnow,owntheflag.per-mailboxEachflagmailboxnextcarriestodelivStatusit(healthy/throttled/paused),iscomplaint/bounce/softBouncetherates;onevendorReputationScore/vendorPlacementRatethataremattersVENDOR-REPORTED,not thenulltop-levelwhenone.unmeasured. Vendor-pool warmup(the underlying reputation-building the provider runs)is FEED-INVISIBLE bydesign: nothing in this response surfaces the vendor's own warmup internals — dailyCap/warmupDay/sendReady here are this platform's own ramp schedule, computed independently of whatever the vendor is doing in its pool, so do not expect a vendor-side warmup event to show up here.design. `messages[]`surfaces system notices this account should act on (e.g. a setup step that needs a retry, a mailbox credential that just went live) — each has kind, severity ('info' = resolves on its own | 'action_required' = nothing progresses until you act, and acting works | 'operator_pending' = the platform has stopped and nothing you change will restart it, but an operator can clear the blocker and then retrying the SAME call with the SAME idempotency key completes it — keep your inputs as they are and do not give up | 'terminal' = the platform has STOPPED, retrying will never help and only a human can move it — the actionHint names contact_operator), body, actionHint (structured — e.g. which tool + idempotencyKey to retry with), createdAt; poll this alongside the mailbox fields — capped at the newestpreviews 5(operator replies sort first, so system churn cannot evict one, but a 6th DISTINCTunackedmessage of either kind can still fall off this preview;noticesuse(severities: list_messagesfor the complete, paginated history). Reading them here does NOT mark them read — only an explicit ack_message call sets readAt. Unacked operator messages sort first here, then system notices newest-first, so a human reply;isreading neverpushed out of the 5 by system churn; list_messages is the full paginatedmarkssurface.read. `nextSteps` nameswhat this account should dothe next—action (seesetup_infrastructure's description for the discriminated shape.setup_infrastructure). Use account/metrics foraccount-widerollups.
⟨18 unchanged words⟩ ), cursor-paginated. Unacked messages sort first (newestfirstwithin that group), then ackedones(also newest first). Returns { messages[], nextCursor }; eachmessage: id, kind, severity ('info'|'action_required'|'operator_pending'|'terminal' —'info''operator_pending'=meansresolvestheonplatformitshasownstopped,|nothing'action_required'you=changeactrestartsand it,andprogressesan|operator'operator_pending'clearing=theplatformblockerstopped,makesonlytheanSAMEoperatorretrycanwork,restartsoit,keepretrying thesameSAMEinputscallandthenidempotencyworkskey;| 'terminal'means the= platformhasstoppedand, only a human can move it,sodo NOT retry), body, actionHint(structured — e.g. which tool + idempotencyKey to retry with), source ('system'|'operator'system|operator), createdAt, readAt. `readAt` is set ONLY byan explicitack_messagecall—LISTING messageslisting never marksthem, here or anywheremessageselseread, socalling this tool leaves every readAt exactly as it was. Aa null readAttherefore means 'not acked', which is NOT evidence thedoesmessagenotwasmean never seen: it may have been read and,simplyonly not yet acknowledged. Use ack_message toacknowledgestop oneby id so it stopsresurfacing. infrastructure_statusalsoinlines the newest 5 unackedmessages for a quick glance —messages; this is the full paginated surface.
Downgrade: release your N NEWEST live mailboxesnow and,lowerlowering the billed quantity. Inputs: count, acknowledged (must be true —this is a quoted, irreversible-this-cycle consent: therelease is immediatefor provisioningbutthere isirreversible;NOno mid-cyclecredit; thecredit, lower pricetakes effectstarts next renewal, minimum 5mailboxes / $99mailboxes/$99). Returns { releasedCount, failedCount, unreleased, billing, deduplicated }where billing is the new projected monthly. `releasedCount` is what COMPLETED and can be less than the `count` you asked for; `failedCount` is how many the provider refused, which are STILL LIVE and still billed, and `unreleased` names those addresses — read them before concluding a downgrade landed. `deduplicated: true` means this call did NOT re-perform the downgrade — every count above describes an EARLIER call's already-recorded outcome under the same idempotencyKey, not new work done just now. This can be true even after the 30-day idempotency window itself has expired: the underlying release record persists independently, so a reused key still reports no re-release rather than silently starting a fresh one.. `count` is RELATIVE— it releases(that many MOREmailboxes,it doesnotset youra fleetto that size — and release cannot be undone through this API, sotarget). ALWAYS pass an idempotencyKey: the FIRST call under a keyresolvesfixes WHICH mailboxes torelease and records that exact set, sorelease;everya later callcarryingwith the SAME key can onlyeverfinish thatsameset.A same-key retry re-attempts the stragglers and`releasedCount` canneverbereleaselessathanmailbox`count`outside—them,`failedCount`whatevernames`count`howyoumanysendthewithvendoritrefused (the recorded setstillwinslive,overbilled;a`unreleased`changedlistscountthem);.aResendretrywithWITHOUTtheasame keyalways releases another `count`.untilA`failedCount`genuineisSECOND0.downgrade`deduplicated:istrue`therefore=aNONEWnewkeywork —reusinganold one can onlyearliereverrecordedfinishoutcomewhatunder that keyalready askedwasfor.replayed. Acall that came back with failedCount above zero did NOT finish, so its key is not frozen: resend the identical request with the same key until failedCount is 0 — from then on that key replays the finished result and releasesgenuinelynothingNEWfurther.downgradeIfneeds acallNEWreturnskey. 409 = a releaseisalreadyrunning for this account: wait, thenrunning;re-readre-check infrastructure_status beforedeciding whether to retry.retrying. To ADD mailboxes use setup_infrastructure/ configure_byo_domainor(request_managed_mailboxes).configure_byo_domain.
⟨17 unchanged words⟩ messageId, deduplicated }. A reply is realsendingsend volumeand is, governedexactlylike campaign sends:itcounts againstthatthemailbox'smailbox daily cap(sentToday +1,visible in infrastructure_status),anditis REFUSED—(never silently dropped—) when the recipient is suppressed, the mailbox is deliverability-paused, or the cap is usedup.upA— a refusal returns { error, code:'send_blocked'send_blocked, reason:'suppressed'|'mailbox_paused'|'daily_cap_reached'suppressed|mailbox_paused|daily_cap_reached, retryable }:; retryable (cap) clearsat thenext daily rollover, non-retryable does not,so— stop retrying,anddodon'tnot loop replies to manufacture volume. Idempotent: identical ⟨8 unchanged words⟩ stable idempotencyKey (else a body hash is used, matched only 10 minutes) so a dropped-response retrycan'tcannot double-send. `deduplicated: true`means= NO new emailwas sent —sent; `messageId` is from an earliersend this call matched. `deduplicated: false` means this platform sent the call onward as a NEW send, which is normally a new email — but it is not a guarantee one left the building: with NO idempotencyKey the body hash is only matched here for 10 minutes, while the sending provider keeps its own record of an identical body on the same thread for longer, and can collapse a much later repeat into the originalmatching send.So onPass along-delayed identical repeat you may get `deduplicated: false` with the ORIGINAL send's messageId and no new email. PASS AN IDEMPOTENCYKEYkey, or vary the body, ifyou needa repeattomust genuinely go out. 404 if no sending mailbox ⟨6 unchanged words⟩
Provision sendinginfrastructureinfra: buybrandedlookalike domains, create mailboxes, start warmup.New mailboxes are ramp-limited server-side — 5 sends/day in week 1, rising to 40/day after 4 weeks — and your own calls cannot exceed that cap; poll infrastructure_status for the current dailyCap.Inputs: brand, primaryDomain, domains + inboxesEachcounts(or distribution), persona, physicalAddress, senderIdentity. Billing isper-provisioned-mailbox ($49 platform +permailbox, graduated: 1-5 at $10, 6-20 at $8, 21+ at $7; 5-mailbox minimum) and the billed quantity follows what you provision here —provisionedpassmailbox; quoteOnly:truefirst to preview the new count + projected monthly price before committing (no silent capacity addition). Every response carries a `billing` projection { provisionedAfter (the live count AFTER this call — reality, not the ask), projectedMonthlyCents, formula }: on quoteOnly it's the preview, on an actual provision it's the real post-provision bill (a capacity-limited partial reflects only what landed). Returns { jobId, billing } — `jobId` is a correlation id only, not a trackable job handle: there is no job store and no endpoint that accepts it, so do not poll for it or treat its presence as meaningful; it exists to correlate this response with logs. A `provisioning` field is present ONLY when the call returned still OWING work, and names which state: 'pending' (a domain's DNS registration is still completing — `pendingDomain` names one of them) or 'capacity_pending' (held at a spend limit, so polling will NOT progress until an operator raises it — call contact_operator instead of waiting). Its ABSENCE is what says the provision finished; the only way to find out is to re-call setup_infrastructure or read infrastructure_status's `nextSteps`. An outcome carrying `provisioning`, and a quoteOnly preview, are deliberately NOT recorded against your idempotencyKey — retrying with the same key re-runs the call and finishes the job rather than replaying the unfinished answer.previews`domains`costandfirst.`inboxesEach``domains`/`inboxesEach` are theinfrastructureinfra you want to HAVE, not an amount to add:each call keeps and resumes what this account already has and buys only the shortfall, so to provision MORE you ask for a LARGER number (domains:2 after a call that provisioned one buys the second; raising inboxesEach tops each domain up). Repeating a call therefore never buys twice, whatever you do with idempotencyKey—resend it, change it, or omit it. The key controls response replay only and has no bearing on what is purchased, soaretry is always safe. A domain this account already registered but that never landed in your account (a prior call that failed after the purchase) is ADOPTED on the retry at zero extra cost rather than bought again. A domain whose DNS setup has not finished yet is recorded and never lost — there is no per-domain DNS field to poll (infrastructure_status reports a domain count, not per-domain detail), so read its `nextSteps` for what to do next, andrepeatthis samecallto converge on the domain and finish its DNS setup. Slot semantics: `domains: N` covers ordinalsnever0..N-1,buysandtwice; each ordinal 0..domains-1 fills to its ownmailbox count (uniform via `inboxesEach`, or per-ordinal via `distribution` — see below); a repeat call at the SAME `domains` provisions nothing new once every ordinal is satisfied, so reaching ordinal 1 needs `domains:2`, not a second `domains:1` call.count. Mailbox addresses are DETERMINISTIC— derivedfrom`persona` + ordinal + slotpersona+ordinal+slot —sokeep`persona` EXACTLY as it was on a resume or retry; changing it mid-account does not move existing addresses but does change what a NEW ordinal derives. `distribution` (optional, alternative to `inboxesEach`) names the per-ordinal mailbox count directly — one entry per domain ordinal, e.g. `[3, 2]` for 3 mailboxes on domain 0 andpersona2unchanged ondomain 1 — forashape `inboxesEach`'s single uniform number cannot express; supply exactly one of the two (an array with `domains` entries, or the uniform count).retry.`registerDomains``registerDomains:true` isthis tenant's opt-inrequired consentto real domain purchases made on the platform's own account (our COGS — your bill is unchanged, mailbox-count-based only); only the operator's own global switch being armed too can ever actually enable it. OMITTING it leavesbefore anyconsent you previously gave UNCHANGED — pass `false` explicitly to revoke it. A call that omitsNEW`registerDomains`domain(orpurchase;sendsomitting it`false`)on abuy-bearing request (one that needs to purchase a newbuydomain)call is refusedBEFORE any spend. Once this account has consented at least once, that refusal is a400`registrar_optin_missing`: 'registerDomains was not set on this request' — resend the identical call withregistrar_optin_missingit(self-correctsetbytoresending trueto self-correct; this is, never an operatorescalation. (An account that has never consented, on a platform where domain registration is not yet enabled at all, instead sees the operator-facing 503 `registrar_unarmed` — indistinguishable from the outside except by status code, but the fix is the same: set `registerDomains: true` and retry.) Separately, a `registerDomains:true` call with NO `registrant` anywhere (neither in this request nor persisted from a prior oneescalation)fails with a 400 `incomplete_registrant` naming the missing fields— alsobefore any purchase. Fresh candidates are de-duplicated against what this account already owns and checked for availability; if a callneedsto buy a domain and no available un-owned candidate exists it fails with a 400 naming that, never a silent repeat purchase (a call that needs to buy nothing is unaffected).`registrant`(a full registrant-of-record object { firstName,lastName, email, phone, addressLine1, city, state, country, postalCode, organization (optional, defaults to brand) }) is OPTIONAL even when `registerDomains` is true whenever a complete registrant isunless already onfile from a prior call — supplying one makes it authoritative for THIS call; omitting it re-uses the persisted one. This platform never invents a domain registrant's legal identity, so a call with no registrant anywhere (neither supplied nor persisted) is rejected naming the missing fields, at or before the actual purchase — never silently. There isfile. NO background retry exists: acall`provisioning`returningresult`provisioning`(pending|capacity_pending) needsthe callerYOU to retryit; the platform does not complete it for you on a timer. Every response carries `nextSteps` (see the discriminatedto`status`/`steps`progress;shape)capacity_pending—needsthecontact_operatoraccount'sinstead.ownReturnsnext{actionjobId,computed fresh each timebilling,so a stalled setup isprovisioning?,nevernextStepssilent.}.
⟨50 unchanged words⟩ persona, physicalAddress, senderIdentity. Billing is per-provisioned-mailbox ($10/mailbox$49 platform +$49perplatformmailbox,mingraduated:51-5 at $10, 6-20 at $8, 21-100 at $6, 101+ at $5; 5-mailbox minimum) and the billed quantity follows what you ⟨952 unchanged words⟩
⟨50 unchanged words⟩ persona, physicalAddress, senderIdentity. Billing is per-provisioned-mailbox ($10/mailbox$49 platform +$49perplatformmailbox,mingraduated:51-5 at $10, 6-20 at $8, 21-100 at $6, 101+ at $5; 5-mailbox minimum) and the billed quantity follows what you ⟨952 unchanged words⟩
⟨96 unchanged words⟩ ,"type":"string"},"sendWindow":{"defaultdescription":{"Integer hours 0-23 (endHour inclusive: the last hour a send may start in) and weekdays, evaluated in `timezone`. Each field you omit (or all of them, by omitting sendWindow) gets the recommended {\"startHour\":238,\"startHourendHour\":016,\"days\":[1,2,3,4,5]} — except on a sandbox account, where omitted fields are open every hour of every day until it upgrades and they take the recommended default. If you set exactly ONE of startHour/endHour, the other still defaults to the recommended value (08/16) — if that combination would wrap past midnight (start > end), the launch is refused; set both explicitly, including for a deliberate overnight window.","properties":{"endHourdays":{"description":"Weekdays sends may go out on, in `timezone`: 0 = Sunday … 6 = Saturday. Omit for [1,2,3,4,5] (every day while the account is a sandbox); include 0 and 6 to send on weekends.","items":{"maximum":236,"minimum":0,"type":"integer"},"startHourmaxItems":7,"minItems":1,"type":"array"},"endHour":{"maximum":23,"minimum":0,"type":"integer"}},"requiredstartHour":[{"startHourmaximum":23,"endHourminimum"]:0,"type":"integer"}},"type":"object"},"sequence":{"items":{"properties ⟨19 unchanged words⟩ ,"type":"integer"},"subject":{"description":"Required on step 1. A LATER step (step > 1) may omit it to keep step 1's subject line (recommended) — it resolves at launch to \"Re: \" + step 1's subject (unchanged if that already starts with \"Re: \"). Set one explicitly on a later step to change only its subject line: every later step is still sent In-Reply-To step 1, though some mail clients display a changed subject as a separate conversation.","maxLength":300,"minLength":1,"type":"string"}},"required":["step","subject","body","delayDays"],"type":"object"} ⟨10 unchanged words⟩ "boolean"},"timezone":{"default":"UTC","description":"IANA time zone the send window is evaluated in — set it to your recipients' zone. Default UTC.","maxLength":100,"minLength":1,"type ⟨8 unchanged words⟩
⟨209 unchanged words⟩ of them) or 'capacity_pending' (held at aspend/plan-slotspend limit, so polling will NOT progress until ⟨797 unchanged words⟩
⟨96 unchanged words⟩ ,"type":"string"},"sendWindow":{"defaultdescription":{"Integer hours 0-23 (endHour inclusive: the last hour a send may start in) and weekdays, evaluated in `timezone`. Each field you omit (or all of them, by omitting sendWindow) gets the recommended {\"startHour\":238,\"startHourendHour\":016,\"days\":[1,2,3,4,5]} — except on a sandbox account, where omitted fields are open every hour of every day until it upgrades and they take the recommended default. If you set exactly ONE of startHour/endHour, the other still defaults to the recommended value (08/16) — if that combination would wrap past midnight (start > end), the launch is refused; set both explicitly, including for a deliberate overnight window.","properties":{"endHourdays":{"description":"Weekdays sends may go out on, in `timezone`: 0 = Sunday … 6 = Saturday. Omit for [1,2,3,4,5] (every day while the account is a sandbox); include 0 and 6 to send on weekends.","items":{"maximum":236,"minimum":0,"type":"integer"},"startHourmaxItems":7,"minItems":1,"type":"array"},"endHour":{"maximum":23,"minimum":0,"type":"integer"}},"requiredstartHour":[{"startHourmaximum":23,"endHourminimum"]:0,"type":"integer"}},"type":"object"},"sequence":{"items":{"properties ⟨19 unchanged words⟩ ,"type":"integer"},"subject":{"description":"Required on step 1. A LATER step (step > 1) may omit it to keep step 1's subject line (recommended) — it resolves at launch to \"Re: \" + step 1's subject (unchanged if that already starts with \"Re: \"). Set one explicitly on a later step to change only its subject line: every later step is still sent In-Reply-To step 1, though some mail clients display a changed subject as a separate conversation.","maxLength":300,"minLength":1,"type":"string"}},"required":["step","subject","body","delayDays"],"type":"object"} ⟨10 unchanged words⟩ "boolean"},"timezone":{"default":"UTC","description":"IANA time zone the send window is evaluated in — set it to your recipients' zone. Default UTC.","maxLength":100,"minLength":1,"type ⟨8 unchanged words⟩
⟨209 unchanged words⟩ of them) or 'capacity_pending' (held at aspend/plan-slotspend limit, so polling will NOT progress until ⟨797 unchanged words⟩