API pipeline: Create, configure, start, stop, measure and delete a Telegram campaign of any kind
Index: API pipelines — index
Goal: create, configure, start, stop, measure and delete a Telegram campaign of any kind. A campaign is a card (card_type: "campaign") that does one job for a project in Telegram, on your oracles and in groups. All kinds share one card, one set of routes and one lifecycle; kind is chosen at create and cannot change.
Kinds
kind |
What it does | Needs to start | Ends | Pipeline |
|---|---|---|---|---|
mtproto_radar (default) |
Listens to groups for messages matching your keywords; matches can go to an oracle for a first message | Pro project; outreach on: oracles + agenda; auto: outreach + 5 canonical matches | When stopped | API pipeline: Listen to Telegram groups for people who need you: create and tune a radar, work its matches and make first contact (draft, send, auto outreach) |
mtproto_ai_scout |
AI reads groups in batches, posts an overview to a channel, can hand picks to a radar | Pro project, agenda, oracles, groups | When stopped | API pipeline: Have AI read groups in batches, post an overview to your channel and hand picks to a radar |
mtproto_announcements |
Posts your posts into groups at a pace set by their traffic | Pro project, groups, oracles, posts, agenda | When stopped | API pipeline: Post your posts into groups at a pace set by their traffic and follow what was sent and deleted |
mtproto_join_chats / mtproto_leave_chats |
Your oracles join or leave a list of groups within Telegram limits | Pro project, groups, oracles | Completed when every oracle walked the list | API pipeline: Have your oracles join or leave a list of Telegram groups within Telegram limits |
mtproto_reactions |
Your oracles react to fresh messages | Pro project, oracles, at least 1 reaction; groups optional | When stopped | API pipeline: Have your oracles react to fresh messages in groups at a chosen pace |
Groups for any kind: API pipeline: Submit your Telegram groups, find groups by keyword, place or rubric, vet them and attach them to a campaign
Steps
1. Create (always Paused).
POST /v0/my/campaigns
Idempotency-Key: camp-join-phuket-1
{"title": "Join Phuket chats", "kind": "mtproto_join_chats",
"project_nonce": "<Pro project>", "tg_groups": ["<preset nonce>"], "oracles": ["<oracle nonce>"]}
→ {"nonce": "470360000000000001", "card_type": "campaign", "kind": "mtproto_join_chats",
"status": "Paused", "is_active": false,
"telegram_groups_list": ["<preset nonce>"], "oracles_list": ["<oracle nonce>"],
"lists": {"telegram_groups": [{…tile…}], "oracles": [{…}], "posts": []}, …}
titlerequired.project_nonce: a project you own or hold x on.- Slots
tg_groups,oracles,poststake card or workspace nonces as strings (not@handles). Oracles need x, groups and posts need r; someone else's Private card is refused. They come back as*_list(nonces) andlists.*(tiles). not_tg_groups: groups the campaign skips, as group or workspace nonces, with the same rules astg_groups; a workspace skips every group inside it. A group in both lists is skipped; an empty list skips nothing. It comes back asnot_telegram_groups_listandlists.not_telegram_groups. Every kind honours it; a reactions campaign without groups reacts in all its oracles' groups except these. Details: API pipeline: Submit your Telegram groups, find groups by keyword, place or rubric, vet them and attach them to a campaignagenda: a swappable agenda you own or may execute (400 otherwise).workspaces: ["<ws>"]places the card into workspaces you can write.- Fixed sets:
speedx_slow | slow | medium | fast | x_fast (default medium);modelinear | magnet;batch_window15m | 30m | 60m | 3h | 6h | 12h | 24h | 72h;batch_messages50–2000;reactions1–16 emoji.
2. Configure. PATCH /v0/my/campaigns/{nonce} with any create field; a slot in the body replaces the whole list. An Active radar or AI scout picks up keyword, exclude, group, project, auto and batch changes while it runs.
3. Start.
POST /v0/my/campaigns/470360000000000001/start
→ {"success": true, "status": "Active", "is_active": true}
Each failed check is 400 with the reason: assign this campaign to a project before activating, assigned project not found, project needs an active Pro membership to run campaigns, assign telegram groups before activating, assign oracles before activating, assign posts before activating, announcements require an agenda assigned: …, assign at least one reaction before activating, ai scout requires an agenda assigned.
- Oracle kinds (announcements, join, leave, reactions): if the campaign cannot start right now, start answers 400 or
{"success": false, "status": "Paused"}and it stays Paused: try again later. Groups or oracles that expand to nothing at run time →Failed. - Radar and AI scout: 200 with the new
status; if the call fails, read the campaign withGETbefore retrying.
4. Stop. POST …/stop → Paused (a radar keeps its config and matches). A 400 means the stop did not go through: call stop again.
5. Measure.
GET /v0/my/campaigns/{nonce}/performance
→ {"totals": {"sent": 40, "deleted": 3, "replied": 5, "reacted": 0, "groups": 25, "oracles": 1,
"joined": 0, "join_pending": 0, "already_member": 0, …, "first_at": "…", "last_at": "…"},
"timeline": [{"day": "2026-10-08", "sent": 0, "joined": 31, "reacted": 40, …}],
"groups": [{"username": "example_group", "sent": 3, "deleted": 2, "replied": 0, "join_status": "already_member", …}],
"join": {"member": 30, "pending": 4, "skipped": 2, "remaining": 0, "oracles": [{…, "flooded_until": null}]}}
GET /v0/my/campaigns/{nonce}/sends?page=1&limit=50
→ {"rows": [{"username": "example_group", "message_id": 17552, "oracle": "…", "post": "…",
"sent_at": "…", "deleted_at": null, "replied_at": "…", "replies": 2}], "total": 40, "has_more": true, …}
Radar results are matches and inbox rooms, not sends. Landing visits by campaign: GET /v0/my/console/traffic/campaign?campaign=. Cabi.Chat Inbox — Leads
6. Delete. Stop it first, then DELETE /v0/my/campaigns/{nonce}.
Statuses: Active, Paused, Completed, Cancelled, Failed. When the project loses Pro, its active campaigns are paused.
One oracle's activity
What one oracle did across all campaigns (owner only, 404 otherwise):
GET /v0/my/oracles/{nonce}/performance: the same shape as a campaign's performance, for this oracle.GET /v0/my/oracles/{nonce}/campaigns→{"rows": [{"campaign": "…", "title": "…", "kind": "mtproto_announcements", "sent": 40, "replied": 5, "joined": 0, "join_pending": 0, "join_banned": 0, "join_dead": 0, "join_flood": 0, "already_member": 0, "left": 0, "reacted": 0, "groups": 25, "first_at": "…", "last_at": "…"}]}: one row per campaign, latest first, at most 100.- Its groups and sent posts:
GET /v0/my/oracles/{nonce}→groups_workspace,sent_posts_workspace; read them withGET /v0/my/workspaces/{ws}?contained_limit=30.
Membership states in these reports: joined, join_pending (join request waits for the admins), join_banned, join_dead (group gone), join_skipped, join_flood (Telegram asked to wait), join_limit (the account hit a Telegram join limit), already_member, left, leave_failed, not_member.
Behind
- Radar and AI scout need no oracle of yours to read groups; the other kinds act through your oracles.
- AI runs (first messages from a radar, AI scout batches, answers to replies on announcements) are AI usage; its cost shows in your AI spend: API pipeline: See your AI spend. Prices: link
Rules (who may do what)
| Action | Needs |
|---|---|
GET /{nonce} |
read |
PATCH, matches/evict, matches/sweep, start, stop |
write |
Anchoring to a project (project_nonce) |
the campaign owner owns the project or holds x on it |
DELETE, performance, sends, matches/{post}/draft, matches/{post}/outreach |
owner only |
GET /v0/my/campaigns lists only campaigns you own. Share one by a link (rights, grant_ttl, who may claim): index, «Sharing by links»; when a w grant ends, the campaign is paused unless another live w grant holds it. Cabi.Chat Graph — Card Policies
API surface
| Endpoint | Purpose | FE | AI / oracle |
|---|---|---|---|
GET /v0/my/campaigns |
Own campaigns as tiles + total (limit, offset, archived) |
yes | yes |
POST /v0/my/campaigns |
Create any kind, Paused | yes | yes |
GET /v0/my/campaigns/{nonce} |
Full card: kind, status, slots, its workspaces | yes | yes |
PATCH /v0/my/campaigns/{nonce} |
Edit config and slots | yes | yes |
DELETE /v0/my/campaigns/{nonce} |
Delete (owner) | yes | rarely |
POST /v0/my/campaigns/{nonce}/start, /stop |
Validate and start; pause | yes | yes |
GET /v0/my/campaigns/{nonce}/performance |
Totals, timeline, per group, join plan | yes | yes, reporting |
GET /v0/my/campaigns/{nonce}/sends |
Each sent message and its fate (page, limit) |
yes | yes |
POST /v0/my/campaigns/{nonce}/matches/… |
Radar: evict, sweep, draft, outreach | yes | see the radar pipeline |
GET /v0/my/oracles/{nonce}/performance, /campaigns |
One oracle's activity | yes | yes, reporting |
GET /v0/my/console/traffic/campaign?campaign= |
Landing visits by campaign | yes | optional |