Version
DESTRUCTIVE

broadcast_message

Send a message through the project's connector to a segment of its members. Destructive and irreversible — preview the audience and get human approval first.

Queue a message through the project's connector to a segment of its members. The send runs in the background at the pace the connector declares (28 messages a second on Telegram); the tool returns the audience it resolved and how many members it will reach.

Destructive and irreversible. Every recipient is a real person and the message lands in their private chat. Call preview_broadcast_audience first, show the human the exact message text, the segment and the recipient count, and get explicit approval before calling this. A broadcast cannot be recalled.

Not idempotent — calling twice sends twice. There is no de-duplication, because two identical broadcasts minutes apart is a legitimate thing a creator may want.

Requires ability

The token behind the MCP session must hold it, or the call is refused with TOKEN_MISSING_ABILITY.

The REST endpoint and this tool share one action, so validation, permissions and events are identical.

Delivered to every endpoint subscribed to it once the change is made.

Annotations

DestructiveOpen world

A client that honours annotations asks a person before running it. It reaches beyond Subscriby: a connector, a provider or a member.

Arguments

project_id*string

UUID of the project whose members will receive the message.

message*string

The message body, 1-4096 characters. Canonical HTML subset only (<b>, <i>, <u>, <s>, <a>, <code>, <pre>, <blockquote>, <tg-spoiler>); anything else is stripped before sending.

audiencestringoptional

Segment to address: all, customer, trialing, lead, churned, expiring_soon, cancelled_still_active, paused, trialing_cardless, all_pass_holders, all_pass_holders_not_in_queue, pass_holders, pass_holders_not_in_queue. Defaults to `all`, which reaches every member with a linked chat.

pass_window_idstringoptional

Required for the single-window pass segments (`pass_holders`, `pass_holders_not_in_queue`); ignored by the others.

plan_idstringoptional

Optional. Narrows the segment to members on one subscription plan, by plan UUID. Composes with the segment rather than replacing it: `customer` plus a plan reaches people paying for that plan right now, `churned` plus a plan reaches people who held it and left. Rejected for `lead` (never subscribed) and for the pass segments (their plan is implied by the window).

expiring_within_daysintegeroptional

Only meaningful for `expiring_soon`. How many days ahead to look, 1-90, default 7. A member is counted only if their access genuinely lapses: an auto-renewing subscription is not expiring.

What it returns

{  "data": {    "project_id": "7f3d1c92-8b45-4e6a-9d21-5c8e0a4b6f13",    "audience": "customer",    "pass_window_id": null,    "plan_id": null,    "expiring_within_days": null,    "recipient_estimate": 128,    "estimated_seconds": 5,    "status": "queued"  }}

status is always queued — this reports what was addressed, not what was delivered. Subscribe to broadcast.completed for the sent and failed tallies.

Narrowing the audience

plan_id composes with audience rather than replacing it: customer plus a plan addresses people paying for that plan right now, churned plus a plan addresses people who held it and left. The plan and the state describe the same subscription, so a member paying for one plan who once trialled another is not matched by the first form.

Four of the segments describe a subscription rather than a member status, which is what a status cannot express — somebody who cancelled but has three weeks left carries the same status as somebody renewing happily:

SegmentAddresses
expiring_soonAccess lapses inside expiring_within_days.
cancelled_still_activeRenewal is off, but time remains.
pausedPaused rather than ended.
trialing_cardlessOn trial with no card on file.

An auto-renewing subscription is never `expiring_soon`

A subscription's end date is rewritten to the new period end on every renewal, so a date inside the horizon describes the next invoice, not an expiry — counting it would place every monthly subscriber in this segment once a month. A member appears only once their access genuinely lapses: renewal is off, or the plan does not renew at all.

Refusals

The tool returns a validation error, and sends nothing, when:

  • the project has no connected bot
  • the body is empty or longer than 4096 characters
  • the audience is unrecognised
  • a pass segment is requested by a project whose plan no longer includes passes
  • a single-window segment is requested with no pass_window_id
  • a plan_id is sent with lead or a pass segment, which cannot be narrowed by plan
  • a plan_id does not belong to the project

A plan filter the segment cannot use is refused rather than ignored: silently dropping it would return a recipient count for a different audience than the one described, and the caller has no way to notice.

How is this guide?

Last updated on