Skip to content

Build and operate outreach with Claude or Codex

Describe the audience, message, timing, and acceptable effect in plain language. Claude or Codex inspects your real Sales setup, builds a reviewable definition, and pauses before any operation that can contact customers.

  • What you'll build — A postponed silence-triggered sequence, a one-time immediate campaign, or a single message/call through the account-scoped Sales-Management MCP.
  • Who it's for — An owner or operator who wants Claude or Codex to configure outreach without manually constructing the internal follow-up graph.
  • Time & plan — About 15–30 minutes. You need an active Sales plan and at least one suitable connected channel for customer contact.

The Sales Platform remains the customer-conversation runtime. Claude or Codex is the configuration and inspection workspace: it can prepare a disabled postponed draft and perform read-only audience previews, but customer contact is always a distinct effect.

What the coding agent can and cannot do

OperationAvailable through MCPCustomer-contact effect
List and inspect follow-upsYesNone
Create or edit a postponed sequenceYes; it is created as draftNone while it remains draft
Publish or unpublish a postponed sequenceYes, through follow_up_updatePublishing lets future matching conversations enter it
Attach a postponed sequence to a sales assistantNo; use the assistant's Toolkit → Follow ups in the appA published sequence only runs for an assistant to which it is attached
Target a postponed sequence by pipelineNo in the current MCP schemaUse target_status_name and target_labels; do not invent pipeline_ids for postponed follow-ups
Preview an immediate audienceYesRead-only selection; nothing is scheduled or sent
Create an immediate campaignYesCreates a real scheduled campaign; now may begin in about three minutes
Pause, resume, or reset an immediate campaignYesChanges a real campaign's lifecycle; reset clears its progress and statistics
Send one message or place one callYesReal and immediate; there is currently no no-effect preview call

There is no universal dry-run switch yet

immediate_outreach_preview_audience previews selection only. It does not simulate channel delivery, message composition, calls, or queue execution. send_one_off_message and outbound_call are the effect when invoked. Keep the client approval prompt enabled and require a separate confirmation containing the exact recipient or audience, channel, content, and timing.

1. Connect the focused Sales areas

Open Agents → Connectors, find the relevant From your sales platform card, and use Connect AI tools to copy the account-specific URL and access key. The key authorizes the account; the domain query parameter narrows the visible tools.

Connect these areas:

MCP areaWhy it is needed
sales_outreachFollow-up CRUD, immediate campaigns, one-off messages, and outbound calls
sales_channelslist_channels and its immediate_outreach_capable signal
sales_clientsReal client IDs, labels, statuses, managers, pipelines, and custom-field values used for targeting

For Claude Code, repeat the generated command with a different local name and domain:

bash
claude mcp add sales-outreach \
  "https://api.mychatbot.app/api/mcp/sales-management?account_id=ACCOUNT_ID&domain=sales_outreach" \
  --transport http \
  --header "Authorization: Bearer mcp_YOUR_ACCESS_KEY"

claude mcp add sales-channels \
  "https://api.mychatbot.app/api/mcp/sales-management?account_id=ACCOUNT_ID&domain=sales_channels" \
  --transport http \
  --header "Authorization: Bearer mcp_YOUR_ACCESS_KEY"

claude mcp add sales-clients \
  "https://api.mychatbot.app/api/mcp/sales-management?account_id=ACCOUNT_ID&domain=sales_clients" \
  --transport http \
  --header "Authorization: Bearer mcp_YOUR_ACCESS_KEY"

Use the URLs generated by your account instead of typing IDs by hand. The corresponding Codex and Cursor snippets are in MCP & connectors. Never put the access key in a prompt, repository, routine, skill, document, or screenshot.

2. State the operating boundary first

Give Claude or Codex an explicit policy before asking it to design anything:

text
Use my MyChatBot Sales MCP connections to build this outreach.

You may inspect assistants, clients, statuses, labels, pipelines, channels, and existing
follow-ups. You may create or edit a postponed follow-up only while it remains draft.
Do not publish a postponed follow-up, create an immediate campaign, send a message,
or place a call until you show me the exact target, content, channel, schedule, pacing,
and resulting effect and I explicitly approve that one operation.

Never guess an ID or channel identifier. Read it from the account first. Preview every
immediate audience and show the count plus sample before asking for approval.

This policy complements the MCP client's write confirmation. It does not replace careful review: a client can be configured to auto-approve writes, and a successful tool call can contact real people.

3. Inspect before authoring

Ask the coding agent to perform these reads:

  1. list_follow_ups — find duplicate or already-running campaigns. Follow next_offset until it is absent when the account has multiple pages.
  2. list_channels — use only enabled rows and, for immediate campaigns, only rows where immediate_outreach_capable=true. Build the ID exactly as <communication_channel>__<assistant_id>.
  3. Use the sales_clients tools to resolve real status, label, manager, pipeline, assistant, and lead IDs. Do not copy a display name into an ID field.
  4. For one-lead actions, call get_client immediately before proposing the send or call so the recipient and available contact details are current.

A useful inspection request is:

text
Inspect existing follow-ups and enabled outreach-capable channels. Then inspect the
status, labels, pipelines, and custom fields available for targeting. Report the exact
IDs and values you would use, but do not write anything yet.

4. Build a postponed sequence as a draft

Claude supplies a plain ordered steps array. The API creates safe node IDs, edges, silence conditions, and editor positions. A complete draft creation can look like:

json
{
  "name": "Proposal follow-up",
  "target_status_name": "Proposal sent",
  "target_labels": ["warm lead"],
  "target_labels_operator": "and",
  "steps": [
    {
      "name": "Gentle reminder",
      "delay_seconds": 7200,
      "chat_depth": 10,
      "instructions": "Briefly remind the client about the proposal and offer to answer questions."
    },
    {
      "name": "Final check-in",
      "delay_seconds": 86400,
      "use_custom_message": true,
      "custom_message": "Just checking in one last time. Reply whenever you would like to continue."
    }
  ],
  "schedule": {
    "timezone": "Europe/Kyiv",
    "days": {
      "Monday": {"start": "09:00", "end": "18:00"},
      "Tuesday": {"start": "09:00", "end": "18:00"},
      "Wednesday": {"start": "09:00", "end": "18:00"},
      "Thursday": {"start": "09:00", "end": "18:00"},
      "Friday": {"start": "09:00", "end": "18:00"},
      "Saturday": {"start": "", "end": ""},
      "Sunday": {"start": "", "end": ""}
    }
  }
}

Call follow_up_create, then immediately call get_follow_up with the returned ID. Ask Claude to summarize:

  • stored status—it must be draft;
  • targeting and working hours;
  • every step in order, including delay and message mode;
  • generated canonical graph fields;
  • anything it could not configure through MCP.

Revise without manipulating the graph

follow_up_update.steps is a complete atomic replacement. To add, edit, delete, or reorder one step, read the stored record, construct the entire intended ordered list, and send that list back:

json
{
  "follow_up_id": "42",
  "steps": [
    {
      "name": "Gentle reminder",
      "delay_seconds": 10800,
      "instructions": "Briefly remind the client about the proposal and offer to answer questions."
    },
    {
      "name": "Final check-in",
      "delay_seconds": 172800,
      "use_custom_message": true,
      "custom_message": "Just checking in one last time. Reply whenever you would like to continue."
    }
  ]
}

Never send a partial steps array expecting it to patch one element. An empty array clears a draft sequence.

Attach and publish separately

The current Sales MCP cannot attach the new sequence to an assistant. In the app:

  1. Open the sales assistant that should execute it.
  2. Open Toolkit → Follow ups and attach the reviewed sequence.
  3. Return to Outreach and inspect the draft once more.

Only after that review, publish with a separate call:

json
{
  "follow_up_id": "42",
  "status": "live"
}

To unpublish and disable the sequence, call follow_up_update with status: "draft". A live configuration change must move it to draft in the same call or in a prior call. Use follow_up_delete only after reading the record and confirming that permanent deletion—not disabling the sequence—is intended.

5. Preview an immediate campaign

Immediate outreach is a one-time campaign. Start with exactly the audience fields you intend to use for creation:

json
{
  "client_source": "allClients",
  "statuses": ["Qualified"],
  "labels": ["annual plan"],
  "labels_operator": "and",
  "pipeline_ids": [7],
  "created_date_from": "2026-08-01",
  "has_email": true,
  "audience_limit": 100,
  "metadata_filters": [
    {
      "field": "country",
      "operator": "in",
      "values": ["Ukraine", "Poland"]
    }
  ]
}

Call immediate_outreach_preview_audience. It returns:

  • matched_count—the audience size after the optional limit;
  • missing_count—the selector's aggregate missing-data count; this is not a delivery-readiness check;
  • sample_clients—up to five {client_id, client_name} rows;
  • the resolved activity cutoff range, where applicable.

Preview is read-only, but it reads the real customer database. Ask Claude to show the payload and result unchanged, then spot-check the sample leads in the app. A preview does not prove that a channel can deliver to every recipient, and it does not reserve the matched set. Re-preview immediately before approval; account data can change between the preview and campaign creation.

6. Create the immediate campaign only after approval

Resolve the delivery channel with list_channels. For example, an Email row with communication_channel: "Email", assistant_id: 15, is_on: true, and immediate_outreach_capable: true becomes Email__15.

Reuse the previewed audience fields exactly and add content, channel, schedule, and pacing:

json
{
  "name": "Annual-plan check-in",
  "client_source": "allClients",
  "statuses": ["Qualified"],
  "labels": ["annual plan"],
  "labels_operator": "and",
  "pipeline_ids": [7],
  "created_date_from": "2026-08-01",
  "has_email": true,
  "audience_limit": 100,
  "metadata_filters": [
    {
      "field": "country",
      "operator": "in",
      "values": ["Ukraine", "Poland"]
    }
  ],
  "outreach_channels": ["Email__15"],
  "instructions": "Write a concise, personal check-in about renewing the annual plan. Ask one clear question and do not invent discounts.",
  "email_subject": "A quick annual-plan check-in",
  "schedule_mode": "later",
  "schedule_run_at": "2099-08-25T09:00:00Z",
  "send_window_start": "09:00",
  "send_window_end": "17:00",
  "send_timezone": "Europe/Kyiv",
  "interval_seconds": 3600,
  "chats_per_interval": 50,
  "send_delay_seconds": 7,
  "dynamic_audience": false
}

Before Claude calls immediate_outreach_create, require it to show a final approval summary containing:

  • matched count and five-lead sample from the preview;
  • the unchanged audience payload;
  • exact ordered channel IDs;
  • exact instruction or custom message and email subject/attachments;
  • UTC or per-lead run time, daily window, and timezone;
  • pacing, total upper bound, and whether the audience is static or dynamic.

Creation returns {follow_up_id, status, stats: {matched}}. A successful response means the real campaign has been stored as scheduled; it is not a dry run.

Replace the deliberately distant example date with the reviewed future run time. Keeping an example far in the future makes an accidental copy less likely to contact customers immediately, but invoking the tool still creates a real campaign.

Static is the safer default

With dynamic_audience=false, creation resolves and freezes the recipient set. A dynamic campaign re-runs filters at send time and may include clients who did not appear in the preview. Use it only when that changing membership is intentional.

Per-lead local time

For a per-lead wall time, set schedule_run_at without Z or an offset and provide the custom-field name holding each lead's IANA timezone or numeric UTC offset:

json
{
  "schedule_mode": "later",
  "schedule_run_at": "2099-08-25T10:00:00",
  "timezone_field": "timezone"
}

Those fields belong inside the otherwise complete create payload. Static campaigns validate the resolved instant for every matched lead before storage. Dynamic campaigns must remain in the future even at UTC+14 because their eventual recipients are unknown.

7. Pause, resume, reset, or inspect

Read the campaign with get_follow_up before every lifecycle change, then use immediate_outreach_update_status for the approved transition.

Pause a scheduled or active campaign:

json
{"follow_up_id": "84", "status": "user_paused"}

Resume only a user_paused campaign:

json
{"follow_up_id": "84", "status": "scheduled"}

Reset a non-running campaign to draft and clear all progress and statistics:

json
{"follow_up_id": "84", "status": "draft"}

An active running, recovering, or system-paused campaign must first move to user_paused before reset. Status changes use optimistic matching; if someone changes the campaign concurrently, read it again instead of repeating the stale request.

8. Handle one-recipient actions deliberately

For one message, resolve the lead with get_client, resolve the channel with list_channels, show the exact recipient/channel/text, and ask for separate approval:

json
{
  "client_id": "CLIENT_UUID_FROM_GET_CLIENT",
  "assistant_id": "15",
  "communication_channel": "Email",
  "message": "Hi Sam, this is a reminder that your appointment is tomorrow at 10:00.",
  "subject": "Appointment reminder"
}

With a messaging channel, calling send_one_off_message sends the real message and writes it to chat history.

Do not select Ringostat, UniTalk, or Binotel when the intended effect is a text message. Those channel values are legacy call-provider paths: invoking send_one_off_message with one of them initiates a real operator callback/direct call, and its message value is not delivered or spoken to the lead.

For one call, resolve a lead with a phone number and an outbound-capable Twilio- or SIP-trunk Calls channel. Confirm the literal opening line—the customer will hear it:

json
{
  "client_id": "CLIENT_UUID_FROM_GET_CLIENT",
  "assistant_id": "15",
  "first_message": "Hi Sam, this is Alex calling about the demo you requested. Is now a good time?"
}

Calling outbound_call initiates the real call. WebSDK Calls channels cannot dial out.

9. Ask Claude for a final handoff

A useful completion request is:

text
Summarize what you inspected and changed. Include every created follow_up_id, current
status, target definition, assigned channel/assistant, schedule, pacing, and whether any
customer contact is now possible. List manual app steps still required. Do not perform
another write while producing the handoff.

Keep the returned IDs. They let another operator call get_follow_up, pause/unpublish, or diagnose the same record without searching by a potentially duplicated name.

See also