Member Tools
Members are a project's subscribers, the people every payment is made for and every access grant resolves to.
Members are a project's subscribers, the people every payment is made for and every access grant resolves to. These tools list and read them, ban, unban and kick, read the accounts they linked, and send a broadcast to a segment of them through the project's connector.
Tools
ban_memberBan MemberDESTRUCTIVEbroadcast_messageBroadcast MessageREADfind_member_by_identityFind Member by IdentityREADget_subscriberGet SubscriberDESTRUCTIVEkick_memberKick MemberREADlist_member_identitiesList Member IdentitiesREADlist_subscribersList SubscribersREADpreview_broadcast_audiencePreview Broadcast AudienceDESTRUCTIVEunban_memberUnban MemberDESTRUCTIVEunlink_member_identityUnlink Member Identityban_member
DESTRUCTIVEBan a project member. Flips status to banned and emits member.banned. Noop when the member is already banned.
Ban a project member. Delegates to an Action so cache invalidation runs and a member.banned event emits with the optional reason. Removal from the gated places runs on the connector's pipeline. Noop when the member is already banned.
Requires ability
The token behind the MCP session must hold it, or the call is refused with TOKEN_MISSING_ABILITY.
Runs the same action as
The REST endpoint and this tool share one action, so validation, permissions and events are identical.
Fires one event
Delivered to every endpoint subscribed to it once the change is made.
Annotations
A client that honours annotations asks a person before running it. Sending the same arguments twice changes nothing the second time. It reaches beyond Subscriby: a connector, a provider or a member.
Arguments
member_id*stringUUID of the project member to ban.
reasonstringoptionalOptional human-readable reason that lands in the member.banned event payload.
What it returns
{ "data": { "id": "2a91c4e7-6f38-4b52-8e0d-9c1a7b3f5d80", "status": "banned" }}How it fails
RESOURCE_NOT_FOUNDunknown member_id, or the member belongs to a team outside the token's scope.
TOKEN_MISSING_ABILITYtoken lacks project-user:update.
broadcast_message
DESTRUCTIVESend 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.
Runs the same action as
The REST endpoint and this tool share one action, so validation, permissions and events are identical.
Fires events
Delivered to every endpoint subscribed to it once the change is made.
Annotations
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*stringUUID of the project whose members will receive the message.
message*stringThe 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.
audiencestringoptionalSegment 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_idstringoptionalRequired for the single-window pass segments (`pass_holders`, `pass_holders_not_in_queue`); ignored by the others.
plan_idstringoptionalOptional. 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_daysintegeroptionalOnly 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:
| Segment | Addresses |
|---|---|
expiring_soon | Access lapses inside expiring_within_days. |
cancelled_still_active | Renewal is off, but time remains. |
paused | Paused rather than ended. |
trialing_cardless | On 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_idis sent withleador a pass segment, which cannot be narrowed by plan - a
plan_iddoes 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.
Find the project member who connected a given account on a connector, from the connector key and the platform's own id.
Turns the id a bot or a server hands over into a Subscriby member: give it the project, the connector the account lives on and the platform's own id for the account, and it answers the same row get_subscriber does, with the member's connected accounts embedded as identities[]. This is the lookup for a workflow that starts from a platform event (a message, a join, a role change) and needs to know which member, if any, that account belongs to. An account nobody in the project has connected is RESOURCE_NOT_FOUND, indistinguishable from an unknown project, so a caller never learns whether an id exists elsewhere. The REST twin is the identity filter on GET /v1/projects/{project}/members, which returns a one-row page instead.
Requires ability
The token behind the MCP session must hold it, or the call is refused with TOKEN_MISSING_ABILITY.
Runs the same action as
The REST endpoint and this tool share one action, so validation, permissions and events are identical.
Annotations
It reads and never changes anything.
Arguments
project_id*stringUUID of the project the member belongs to.
connector*stringConnector key the account lives on, as `list_connectors` lists them.
external_id*stringThe platform's own id for the account, as `list_member_identities` shows it.
What it returns
{ "data": { "id": "2a91c4e7-6f38-4b52-8e0d-9c1a7b3f5d80", "project_id": "7f3d1c92-8b45-4e6a-9d21-5c8e0a4b6f13", "status": "customer", "name": "Ada Lovelace", "identities": [ { "id": "6a1f0c3e-2d4b-4e8a-9c7d-1b5e3f9a2c4d", "connector": "telegram", "source": "handshake", "preferred": true, "external_id": "123456789", "display_name": "Ada Lovelace", "username": "ada", "linked_at": "2026-09-12T10:05:00Z" } ], "created_at": "2026-05-18T10:40:00Z" }}The row is the one get_subscriber and list_subscribers render; identities[] lists every account the member connected, not only the one asked about.
How it fails
RESOURCE_NOT_FOUNDunknown project_id, a project outside the token's scope, or an account nobody in the project has connected.
TOKEN_MISSING_ABILITYtoken lacks project-user:view-any.
get_subscriber
READOne project member by UUID, in the list_subscribers row shape. Returns two email addresses verbatim — scrub before forwarding.
Fetch one member (subscriber) of a project by id when you already know it — from a webhook payload's
subscriber_id, a support conversation, or an earlier list. The row is the one
list_subscribers returns, so a member read here is identical to
the same member read from the list.
PII
Both email (member-chosen and verified) and billing_email (whatever they
gave a payment provider) come back verbatim, as do the platform ids of the member's connected
accounts under identities.
Scrub them before forwarding to any third-party LLM, log sink or analytics
surface.
Requires ability
The token behind the MCP session must hold it, or the call is refused with TOKEN_MISSING_ABILITY.
Runs the same action as
The REST endpoint and this tool share one action, so validation, permissions and events are identical.
Annotations
It reads and never changes anything.
Arguments
member_id*stringUUID of the project member to fetch.
What it returns
{ "data": { "id": "2a91c4e7-6f38-4b52-8e0d-9c1a7b3f5d80", "project_id": "7f3d1c92-8b45-4e6a-9d21-5c8e0a4b6f13", "name": "Ada Lovelace", "email": "[email protected]", "email_verified": true, "billing_email": "[email protected]", "identities": [ { "connector": "telegram", "external_id": "123456789", "display_name": "Ada Lovelace", "username": "ada", "preferred": true } ], "status": "customer", "joined_at": "2026-05-18T10:05:00+00:00" }}name is the display name Subscriby derived for the member; status is their standing in the
project (lead, customer, and the churned and moderation states), not a subscription status.
identities lists the member's connected accounts, the one they prefer to be reached on first: the
connector key, the platform's own id for the account, and the display name and username the platform
reported. A member who only ever used the portal and connected nothing has an empty list.
How it fails
RESOURCE_NOT_FOUNDunknown member_id, or a member of a project outside the token's scope.
AUTHENTICATION_REQUIREDno authenticated user on the request.
TOKEN_MISSING_ABILITYtoken lacks project-user:view.
kick_member
DESTRUCTIVEKick a project member without banning them. Status rolls to churned; the subscriber can re-join later.
Kick a project member without a permanent ban. Status rolls to churned; removal from the gated places happens on the connector's pipeline. The subscriber can re-join via a fresh purchase or access code later. Delegates to an Action so cache invalidation runs and member.kicked emits with the optional reason.
Requires ability
The token behind the MCP session must hold it, or the call is refused with TOKEN_MISSING_ABILITY.
Runs the same action as
The REST endpoint and this tool share one action, so validation, permissions and events are identical.
Fires one event
Delivered to every endpoint subscribed to it once the change is made.
Annotations
A client that honours annotations asks a person before running it. Sending the same arguments twice changes nothing the second time. It reaches beyond Subscriby: a connector, a provider or a member.
Arguments
member_id*stringUUID of the project member to kick.
reasonstringoptionalOptional human-readable reason that lands in the member.kicked event payload.
What it returns
{ "data": { "id": "2a91c4e7-6f38-4b52-8e0d-9c1a7b3f5d80", "status": "churned" }}How it fails
RESOURCE_NOT_FOUNDunknown member_id, or the member belongs to a team outside the token's scope.
TOKEN_MISSING_ABILITYtoken lacks project-user:update.
List the platform accounts a project member has connected, with which one the project reaches first.
Lists a member's connected accounts on the connectors: the platform (connector), the platform's own id, name and handle for the account, how the link was proven (source) and whether it is the one the project reaches first (preferred). Members connect accounts themselves — from the portal's Account & Recovery screen or by talking to the project's bot — so there is no tool to connect one for them. The same rows are embedded as identities[] in get_subscriber's REST counterpart, GET /v1/projects/{project}/members/{member}.
Requires ability
The token behind the MCP session must hold it, or the call is refused with TOKEN_MISSING_ABILITY.
Runs the same action as
The REST endpoint and this tool share one action, so validation, permissions and events are identical.
Annotations
It reads and never changes anything.
Arguments
member_id*stringUUID of the project member whose connected accounts to list.
What it returns
{ "data": [ { "id": "6a1f0c3e-2d4b-4e8a-9c7d-1b5e3f9a2c4d", "connector": "telegram", "source": "handshake", "preferred": true, "external_id": "123456789", "display_name": "Ada Lovelace", "username": "ada", "linked_at": "2026-09-12T10:05:00Z" } ]}id is the link, which is what unlink_member_identity takes; external_id is the platform's id for the account. source is one of handshake (connected from the portal and confirmed by the bot, or a portal sign-in), adopted (taken over from a sibling project), portal, bot (the bot met the account first) or backfill (migrated from before connectors).
How it fails
RESOURCE_NOT_FOUNDunknown member_id, or the member belongs to a team outside the token's scope.
TOKEN_MISSING_ABILITYtoken lacks project-user:view.
list_subscribers
READPaginated list of project members with optional filters by project, status, and free-text search.
List members (project subscribers) with optional filters by project_id, status, and a free-text search across name, email and billing email. Results are scoped to the token's team and ordered newest-first.
Emails are returned verbatim. Scrub before forwarding to external systems.
Requires ability
The token behind the MCP session must hold it, or the call is refused with TOKEN_MISSING_ABILITY.
Runs the same action as
The REST endpoint and this tool share one action, so validation, permissions and events are identical.
Annotations
It reads and never changes anything.
Arguments
project_idstringoptionalOptional project UUID to narrow the result.
statusstringoptionalMember status filter. One of: lead, trialing, customer, churned, banned
leadtrialingcustomerchurnedbannedsearchstringoptionalCase-insensitive partial match against name + email.
limitintegeroptionalMaximum members to return per page (1..100).
1max100pageintegeroptional1-indexed page number.
1What it returns
{ "data": [ { "id": "2a91c4e7-6f38-4b52-8e0d-9c1a7b3f5d80", "project_id": "7f3d1c92-8b45-4e6a-9d21-5c8e0a4b6f13", "name": "Jane Doe", "email": "[email protected]", "email_verified": true, "billing_email": "[email protected]", "identities": [ { "connector": "telegram", "external_id": "123456789", "display_name": "Jane Doe", "username": "janedoe", "preferred": true } ], "status": "customer", "joined_at": "2026-05-18T10:05:00Z" } ], "meta": { "page": 1, "limit": 25, "total": 41, "has_more": true }}How it fails
TOKEN_MISSING_ABILITYtoken lacks project-user:view-any.
RESOURCE_NOT_FOUNDproject_id names a project the token cannot see: unknown, another team's, or outside the token's scope:project: allow-list. Without a project_id, the list spans only the projects the allow-list admits.
VALIDATION_FAILEDstatus is not one of the member statuses; the error context lists the supported values.
Caveats
emailandbilling_emailmean different things.emailis an address the member chose and verified, and is their portal sign-in credential.billing_emailis whatever they typed at a payment provider's checkout — never verified, never used for authentication. Do not treat abilling_emailas a confirmed way to reach someone.- Most members have
email: null. They join through a bot and are never asked for one, sobilling_emailis often the only address on record — which is what makes it useful for matching a refund request to a subscription. billing_emailisnullfor anyone who joined by redeeming an access code, because no payment ever took place. It is alsonullfor members whose last payment predates automatic capture.
Size a broadcast without sending it. Returns every audience segment with its current recipient count.
Read-only. Returns how many members a broadcast would reach, either for one named segment or for every segment at once. Nothing is sent.
Call this before broadcast_message, every time, and show the human the count. An audience that silently resolves to everyone is the one broadcast mistake that cannot be undone.
Requires ability
The token behind the MCP session must hold it, or the call is refused with TOKEN_MISSING_ABILITY.
Runs the same action as
The REST endpoint and this tool share one action, so validation, permissions and events are identical.
Annotations
It reads and never changes anything.
Arguments
project_id*stringUUID of the project to size a broadcast for.
audiencestringoptionalSize one segment only. Omit to receive every segment with its count.
pass_window_idstringoptionalNeeded to size the single-window pass segments; without it they report a null count.
plan_idstringoptionalOptional plan UUID. Sizes each segment as narrowed to that plan. Segments that cannot use a plan filter (`lead`, the pass segments) report their unnarrowed count and a null plan_id.
expiring_within_daysintegeroptionalHow far ahead `expiring_soon` looks, 1-90, default 7. Ignored by every other segment.
What it returns
{ "data": { "project_id": "7f3d1c92-8b45-4e6a-9d21-5c8e0a4b6f13", "has_connected_bot": true, "segments": [ { "value": "customer", "label": "Customers Only", "description": "Members with an active paid subscription.", "requires_pass_window": false, "supports_plan_filter": true, "requires_expiring_within_days": false, "plan_id": null, "expiring_within_days": null, "recipient_estimate": 128, "estimated_seconds": 5 } ] }}Omitting audience returns every segment:
A segment that addresses a single window reports recipient_estimate: null unless a pass_window_id is supplied — it cannot be sized without knowing which window, and a misleading 0 would read as "nobody holds this".
supports_plan_filter and requires_expiring_within_days say which extra inputs each segment accepts, so an agent can decide what to ask a human for before proposing anything.
Unlike broadcast_message, a plan_id a segment cannot use is dropped for that segment rather than refused — the no-audience form sizes every segment in one call, and refusing would make a plan filter unusable for the very listing used to choose a segment. Each row reports the plan_id it actually applied, so nothing is silently ignored.
Check has_connected_bot before proposing a send: a project without one cannot broadcast at all.
unban_member
DESTRUCTIVELift a ban on a project member. Status rolls to churned; next successful payment promotes them back to customer.
Lift a ban on a project member. Status moves from banned to churned — if the subscriber still has active subscriptions, the next successful-payment transition promotes them to customer automatically. Delegates to an Action so cache invalidation runs.
Requires ability
The token behind the MCP session must hold it, or the call is refused with TOKEN_MISSING_ABILITY.
Runs the same action as
The REST endpoint and this tool share one action, so validation, permissions and events are identical.
Fires one event
Delivered to every endpoint subscribed to it once the change is made.
Annotations
A client that honours annotations asks a person before running it. Sending the same arguments twice changes nothing the second time. It reaches beyond Subscriby: a connector, a provider or a member.
Arguments
member_id*stringUUID of the banned project member to unban.
What it returns
{ "data": { "id": "2a91c4e7-6f38-4b52-8e0d-9c1a7b3f5d80", "status": "churned" }}How it fails
RESOURCE_NOT_FOUNDunknown member_id, or the member belongs to a team outside the token's scope.
TOKEN_MISSING_ABILITYtoken lacks project-user:update.
unlink_member_identity
DESTRUCTIVEDisconnect one of a project member's platform accounts, keeping them a way to sign in.
Disconnects one of a member's connected accounts on the creator's behalf: the account no longer signs the member in to the portal, and the project's bot no longer knows them by it. The same recovery rule the portal applies to the member holds here — the account cannot be removed when it is the member's last way to sign in (no verified email, linked Google account or other connected account remains). Emits member.identity_unlinked. Destructive and not idempotent: a second call answers RESOURCE_NOT_FOUND.
Requires ability
The token behind the MCP session must hold it, or the call is refused with TOKEN_MISSING_ABILITY.
Runs the same action as
The REST endpoint and this tool share one action, so validation, permissions and events are identical.
Fires one event
Delivered to every endpoint subscribed to it once the change is made.
Annotations
A client that honours annotations asks a person before running it.
Arguments
member_id*stringUUID of the project member.
identity_id*stringThe link `id` from `list_member_identities` to disconnect.
What it returns
{ "data": { "member_id": "2a91c4e7-6f38-4b52-8e0d-9c1a7b3f5d80", "identity_id": "6a1f0c3e-2d4b-4e8a-9c7d-1b5e3f9a2c4d", "connector": "telegram", "removed": true }}How it fails
RESOURCE_NOT_FOUNDunknown member_id, an identity_id that is not one of that member's links, or a member outside the token's scope.
VALIDATION_FAILEDthe account is the member's last way to sign in. Have them add an email or connect another account first.
TOKEN_MISSING_ABILITYtoken lacks project-user:update.
How is this guide?
Disaster Recovery Tools
Disaster Recovery is Subscriby's answer to a connector outage or a lost channel: what the probes found, what was recovered and whether it can still be undone.
Observability Tools
Where to look when something did not happen: webhook endpoints and their deliveries, the activity log of every mutation, and the status of a job another tool queued.