accountread-only | Account overview: brand, plan, status, vertical ('crowdfunding' or null — set at signup), billingState, activationState, resource counts, usageCents, quota, deliverability (loop state: paused/throttled mailboxes, burning domains, auto-replacements, recentActions[]), and teardown (reclaim summary once canceled, else null). Billing is per-provisioned-mailbox on the live provisioned count: $49 platform + per mailbox, graduated: 1-5 at $10, 6-20 at $8, 21+ at $7; 5-mailbox minimum ($99 at 5); the billed quantity tracks the real provisioned count (deprovision lowers it). activationState is the actual send state — trust it over 'sent' counts: 'active' = real sending live; 'pending_provisioning' = paid but infrastructure still being armed, sends shown are sandbox previews that DON'T leave; 'capacity_pending' = provisioning held at a spend limit; 'screening_hold' = account under review; 'sandbox' = demo/free. Use metrics for counts, infrastructure_status for per-mailbox health. |
ack_message | Acknowledge a message by id (from list_messages or infrastructure_status's messages[]) — sets it read so it stops surfacing as unacked. THIS IS THE ONLY THING THAT SETS readAt: reading or listing messages never marks them, so a message stays unacked until you call this explicitly. Idempotent: acking an already-acked id returns success with no second effect, never an error. 404 if the id doesn't exist for this tenant. |
activityread-only | Unified activity feed: campaign events (sent/reply/bounce/...) merged with deliverability loop actions (pause/throttle/replace-domain). Cursor-paginated → { items[], nextCursor }; each item { id, kind:'event'|'deliverability', label, ts, target, detail }. Filters: kind, limit (default 50, max 200). Use inbox for replies only. |
add_leadsdestructive | 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 address on your suppression list (stored as suppressed, never emailed), an address repeated in the request. Adding never resumes a paused campaign; repeating the call adds nothing; a campaign holds at most 5000 contacts. A trial account can launch up to 5 test campaigns in any 24 hours, each holding up to 100 contacts, which is enough for a small test. Both are limits of the trial, not of Coldrig: going live removes them. Nothing in a campaign launched on the trial is ever really sent: when the account goes live, the emails it still has waiting are canceled and it cannot be resumed or added to, so launch a new campaign to send real email. People who already got this email are skipped: a contact is left out, never stored or emailed, when another campaign of this account has 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 the account is still on the trial, and the built-in demo campaign never counts. alreadyEmailed in the answer is how many were skipped (anyone also on your suppression list is counted there, not in suppressed): tell the person in plain words, for example "skipped 12 people 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 same campaign with add_leads and includeAlreadyEmailed: true. Never launch the same list again to include them: that emails everyone who was not skipped a second time. 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 campaign with exactly the same set of contact addresses and the same subjects 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. repeatOf states a deliberate repeat, exactly as on launch_campaign. Returns { campaignId, added, suppressed, alreadyInCampaign, alreadyEmailed, duplicatesRemoved, contacts } — contacts is the campaign's total afterwards. |
campaign_resultsread-only | Outcome counts for ONE campaign. Input: campaignId (from launch_campaign). Returns { campaignId, sent, reply, bounce, complaint, unsubscribe, failed, soft_bounce, auto_reply } — event counts, bounce = HARD only, soft_bounce separate, opens not 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. |
configure_byo_domaindestructive | Register or advance a BYO domain/mailbox 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 DNS delegation, advances pending_dns -> active, or -> abandoned after 7 idle days) | acknowledge_consent (needs id + acknowledged:true — required before a primary domain can proceed past pending_consent; documents informed consent, does not remove exposure) | request_managed_mailboxes (needs id + count — platform-provisioned mailboxes on an ALREADY-ACTIVE domain; every response carries a billing projection { provisionedAfter, projectedMonthlyCents, formula }; quoteOnly:true previews without provisioning) | connect_mailbox (needs id + email + transport — declares an EXISTING OAuth/SMTP+IMAP connection, bypassing provisioning; transport is smtp | gmail_api | ms_graph, each with its own credential fields). |
configure_dashboarddestructive | Write a saved dashboard view. action = create (needs name+layout) | update (needs id+rev+layout; optional name renames) | promote (id → default) | delete (id). update is rev-CAS: a stale rev returns { currentRev, currentLayout } to rebase and retry. Optional note. Read the current rev+layout via get_dashboard first. |
configure_webhookdestructive | Manage an outbound webhook subscription. action = create (needs url + eventTypes: reply|bounce|soft_bounce|complaint|unsubscribe; optional secret/active) | update (needs id + one changed field; active:true re-enables an auto-disabled one, active:false pauses; secret rotates) | delete (needs id). create/rotate return the HMAC signing secret ONCE. URLs must be https to a public host (private/metadata IPs rejected). Deliveries are signed X-Coldrig-Signature: sha256=HMAC-SHA256(secret, raw body). |
contact_operator | Reach a human operator for anything list_messages/infrastructure_status cannot answer (a stuck vendor issue, a billing question, an account-level ask). Inputs: body (1-2000 chars), urgency (normal|needs_human, default normal). Files a ticket, notifies the operator; returns { ticketId, note, deduplicated }. Works in every account state a token authenticates in (dunning-suspended, canceling, canceled) — admin-terminated is the one exception, rejected at auth. The reply arrives as a message on THIS account (poll list_messages/infrastructure_status — no reply-fetch call). Sending the IDENTICAL body+urgency again within an hour returns the SAME ticketId, no second ticket/alert (`deduplicated: true`). TEXT MATCH, NOT AN INTENT MATCH: a genuinely new message with identical wording collapses the same way, silently — vary the wording (or raise urgency, always a new ticket). `needs_human` bypasses the ~10-min ops-email throttle. Rate-limited to 5/hour/tenant — a 429 names retryAfter (seconds). |
disconnect_appdestructive | 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 }. |
get_byo_domainsread-only | List your BYO (bring-your-own) domains, or (with id) one domain's full intake detail. No id → [{ domainId, domain, isPrimary, dnsMode, byoStatus, breakerTier, reputationBranch, mailboxCount }]. With id → adds the pre-flight scan result, abuse-gate verdict, and consent-acknowledgment status. byoStatus progresses pending_kyc|pending_consent|pending_dns → active (or rejected/abandoned). Use configure_byo_domain to register a new one or advance it. |
get_connected_appsread-only | 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. |
get_dashboardread-only | Read saved dashboard views. No id → list all: [{ id, name, isDefault, rev, editedBy }]. With id → that view's full layout + rev (pass this rev as the CAS base to configure_dashboard update). Views are both agent- and human-editable; write them with configure_dashboard. |
get_webhooksread-only | List your outbound webhook subscriptions, or (with id) one subscription plus its recent delivery + attempt log. No id → [{ id, url, eventTypes, active, status, disabledReason, consecutiveFailures }]. With id → { subscription, recentDeliveries[], recentAttempts[] }. Secrets are never returned on reads — they are shown once at create/rotate. |
inboxread-only | Unified reply inbox across mailboxes. Cursor-paginated → { threads[], nextCursor }; each row: threadId, campaignName, leadEmail, subject, mailboxEmail, label, lastEventType, markStatus. Filters: mailbox, campaign, label, read, includeNonreply (threads whose last event is a bounce, complaint or automatic reply such as out-of-office; default true), archived (exclude|include|only). Use thread for one thread's history. |
infrastructure_statusread-only | Warmup + provisioning progress per mailbox. Returns { domains, mailboxes, sendReady, mailboxHealth[], messages[], nextSteps }. New mailboxes ramp-limit server-side: 5 sends/day week 1, 40/day week 4 — dailyCap/sentToday show real capped sending from day 1, never zero. Per-mailbox `sendReady` is a FULLY-RAMPED flag, NOT a send gate — below-ramp mailboxes still send, capped at their own dailyCap; read dailyCap/sentToday for capacity. Top-level `sendReady` is the AND across ALL mailboxes (true once every one is ramped) — for one mailbox's capacity use its own flag. Each mailbox carries delivStatus (healthy/throttled/paused), complaint/bounce/softBounce rates; vendorReputationScore/vendorPlacementRate are VENDOR-REPORTED, null when unmeasured. Vendor-pool warmup is FEED-INVISIBLE by design. `messages[]` previews 5 unacked notices (severities: list_messages); reading never marks read. `nextSteps` names the next action (see setup_infrastructure). Use account/metrics for rollups. |
label_thread | Set or clear a triage LABEL on an inbox thread — the same chip the dashboard shows. Inputs: threadId, label (string; pass label:null to clear). Distinct from mark (read/unread/archived state): a label is a free-form category, not a read flag. Filterable via inbox's label param. |
launch_campaigndestructive | Create and activate a campaign on a lead list. You supply name, offer, leads[], sequence[] (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: "), or set one explicitly to change only that step's 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), 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 `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; 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, if a subject or body uses {{firstName}} or a {{key}} that any lead has no non-empty value for — no fallback word is invented; it is also refused if any subject or body uses a {{…}} that is not one word, a 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` (IANA, e.g. America/New_York; default UTC) — set it to your recipients' zone. sendWindow is { startHour, endHour, days? }: integer hours 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 omitting sendWindow) gets the platform's RECOMMENDED default {"startHour":8,"endHour":16,"days":[1,2,3,4,5]} — Monday-Friday business hours — except on a sandbox account, where omitted fields are open every hour of every day until it upgrades and they take that default; the decision is yours, e.g. include 0 and 6 in days to send on weekends. Setting exactly ONE of startHour/endHour combines it with the recommended default for the OTHER (08/16, including on a sandbox account, since that value re-resolves to the same default on upgrade) — if that combination would wrap the window past midnight (start > end), the launch is refused; set both explicitly, including for a deliberate overnight window such as {startHour:22, endHour:6}. Step 1 goes out from the least-loaded mailbox; each later step goes out delayDays after the previous step ACTUALLY sent, from the SAME mailbox, with In-Reply-To/References set to step 1's Message-ID — it waits while that mailbox is at its daily cap, and the 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, which this build never sends from; a step 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 actually applied; contacts 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 send round, counted as if nothing else were sending), and warning is one plain sentence saying so when it is above 0, or saying that other campaigns' waiting emails, this campaign's own follow-ups, or more people at one company than there are sending days before endBy may still make some contacts miss endBy: tell the person before they confirm. Nothing is refused for it; whatever still waits at endBy is skipped. previewOnly:true 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 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 account has 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 the account is still on the trial, and the built-in demo campaign never counts. alreadyEmailed in the answer is how many were skipped (anyone also on your suppression list is counted there, not in suppressed): tell the person in plain words, for example "skipped 12 people 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 same campaign with add_leads and includeAlreadyEmailed: true. Never launch the same list again to include them: that emails everyone who was not skipped a second time. 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 campaign with exactly the same set of contact addresses and the same subjects 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, resend it with the same idempotency key, which replays the original result instead. A campaign launched on a trial 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 any 24 hours, each holding up to 100 contacts, which is enough for a small test. Both are limits of the trial, not of Coldrig: going live removes them. Nothing in a campaign launched on the trial is ever really sent: when the account goes live, the emails it still has waiting are canceled and it cannot be resumed or added to, so launch a new campaign to send real email. |
list_campaignsread-only | List every campaign at once: [{ campaignId, name, status, counts{sent,reply,bounce,complaint,unsubscribe,failed,soft_bounce,auto_reply} }], newest first — no per-campaign lookup needed. Use campaign_results for one campaign's counts, metrics for account-wide totals. |
list_leadsread-only | List/export leads with their contact-level disposition, cursor-paginated. Returns { leads[], nextCursor }; each row: leadId, email, firstName, company, campaignId, campaignName, globalStatus, interestStatus, notes, tags, suppressed, lastEventType, lastEventTs, 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 IS the export surface — paginate to dump the full book of business as JSON (no separate CSV endpoint). Use update_lead to write disposition, suppress_lead to opt an address out. |
list_messagesread-only | List this tenant's system + operator messages (a retryable setup step, a credential going live, an operator notice), cursor-paginated. Unacked messages sort first (newest within that group), then acked (also newest first). Returns { messages[], nextCursor }; each: id, kind, severity ('info' = resolves on its own | 'action_required' = act and it progresses | 'operator_pending' = platform stopped, only an operator can restart it, retrying the SAME call then works | 'terminal' = platform stopped, only a human can move it, do NOT retry), body, actionHint, source (system|operator), createdAt, readAt. `readAt` is set ONLY by ack_message — listing never marks messages read, so a null readAt does not mean never seen, only not yet acknowledged. Use ack_message to stop one resurfacing. infrastructure_status inlines the newest 5 unacked messages; this is the full paginated surface. |
mark | Set a thread's READ-STATE for inbox triage. Inputs: threadId, status = 'read' | 'unread' | 'archived' (archived hides it from the default inbox; refetch with inbox archived='include'/'only'). Returns { marked: true }. 404 if unknown. This is the read/archive flag ONLY — use label_thread for a triage label chip, reply to respond. |
metricsread-only | Account-wide outcome totals across ALL campaigns: { sent, reply, bounce, complaint, unsubscribe, failed, soft_bounce, auto_reply } — the same event counts as campaign_results but summed tenant-wide (no per-contact fields) (bounce = hard only, opens not tracked; auto_reply = automatic replies such as out-of-office, never counted in reply). Use campaign_results for one campaign, list_campaigns per-campaign, or account for billing/quota. |
pausedestructive | Pause ONE campaign: its status → 'paused' and nothing more in it sends; its remaining steps are HELD, not cancelled (already-sent mail is unaffected) — call resume to continue it. Input: campaignId. Returns { 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 for good (resume does not bring them back), 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. |
pause_alldestructive | Pause EVERY active campaign for the tenant at once (each active status → 'paused'; nothing more in them sends, and their remaining steps are held). No inputs. Returns { pausedAll: true }. Use pause to pause a single campaign by id, and resume (one call per campaign) to continue one. |
plan_campaignread-only | 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 bookingDate; 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 (an opted-in list may cost less on an email-service provider). A bookingDate after the last campaign day is refused 400. Campaign checkout and booking are not available: to book, contact support (contact_operator). |
remove_mailboxesdestructive | Downgrade: release your N NEWEST live mailboxes, lowering the billed quantity. Inputs: count, acknowledged (must be true — release is immediate but irreversible; no mid-cycle credit, lower price starts next renewal, minimum 5 mailboxes/$99). Returns { releasedCount, failedCount, unreleased, billing, deduplicated }. `count` is RELATIVE (that many MORE, not a fleet target). ALWAYS pass an idempotencyKey: the FIRST call under a key fixes WHICH mailboxes to release; a later call with the SAME key can only finish that set. `releasedCount` can be less than `count` — `failedCount` names how many the vendor refused (still live, billed; `unreleased` lists them). Resend with the same key until `failedCount` is 0. `deduplicated: true` = NO new work — an earlier recorded outcome under that key was replayed. A genuinely NEW downgrade needs a NEW key. 409 = a release already running; re-check infrastructure_status before retrying. To ADD mailboxes use setup_infrastructure or configure_byo_domain. |
replydestructive | Send a reply on an existing thread, from the mailbox that sent it. Inputs: threadId, body. Returns { messageId, deduplicated }. A reply is real send volume, governed like campaign sends: counts against the mailbox daily cap, and is REFUSED (never silently dropped) when the recipient is suppressed, the mailbox is deliverability-paused, or the cap is used up — a refusal returns { error, code: send_blocked, reason: suppressed|mailbox_paused|daily_cap_reached, retryable }; retryable (cap) clears next daily rollover, non-retryable does not — stop retrying, do not loop replies to manufacture volume. Idempotent: identical retries collapse to one send — pass a stable idempotencyKey (else a body hash is used, matched only 10 minutes) so a dropped-response retry cannot double-send. `deduplicated: true` = NO new email sent; `messageId` is from an earlier matching send. Pass a key, or vary the body, if a repeat must genuinely go out. 404 if no sending mailbox is on record for the thread. |
resumedestructive | 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 actually sent, so a pause never puts two steps in one day. Leads stopped on their own stay stopped: stopped with pause leadEmails, replied, unsubscribed, bounced or suppressed. Steps a campaign lost while it was paused before this tool existed are not restored. |
setup_infrastructure | Provision sending infra: buy lookalike domains, create mailboxes, start warmup. Inputs: brand, primaryDomain, domains + inboxesEach (or distribution), persona, physicalAddress, senderIdentity. Billing is per provisioned mailbox; quoteOnly:true previews cost first. `domains`/`inboxesEach` are the infra 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` is required consent before any NEW domain purchase; omitting it on a buy call is refused 400 registrar_optin_missing (self-correct by resending true, never an operator escalation) — also needs `registrant`, unless already on file. NO background retry exists: a `provisioning` result (pending|capacity_pending) needs YOU to retry to progress; capacity_pending needs contact_operator instead. Returns { jobId, billing, provisioning?, nextSteps }. |
submit_feedback | 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 }. |
suppress_leaddestructive | Permanently suppress an email address tenant-wide (every current and future campaign) — the manual/free-text 'stop emailing me' path for opt-outs the strict typed-unsubscribe matcher misses. Inputs: email, reason (fixed 'manual' — the only value this tool claims; bounce/complaint/unsubscribe are recorded automatically elsewhere), note (accepted, not persisted). Cancels every pending send + marks every campaign-lead row 'suppressed'. Last-write-wins: re-suppressing a bounce/complaint/unsubscribe row relabels its reason to 'manual'. There is no un-suppress tool. |
threadread-only | Full message history for ONE thread. Input: threadId (from inbox). Returns { threadId, campaignId, leadId, leadEmail, mailboxEmail (null before first send), messages[] }, each message { type (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 first. 404 if unknown. Use inbox to LIST threads; reply to respond; mark/label_thread to triage. |
update_lead | Record what you learned about a contact (their reply, your triage) as a durable, contact-level disposition — keyed by email, visible across every campaign that lists them. Inputs: email, interestStatus (none|interested|meeting_booked|not_now|not_interested|bad_fit|out_of_office|wrong_person — a server-enforced enum; 'do not contact' is NOT a member, use suppress_lead instead), notes, tags (free-form). A PARTIAL patch — only the fields you pass are changed; at least one of interestStatus/notes/tags is required. Filterable via list_leads. |