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)
Index: API pipelines — index
Goal: hear people in Telegram groups asking for what your project offers, collect their messages as matches, tune the radar, and make first contact: a draft you approve, or automatic outreach by your oracle.
Lifecycle shared by all kinds: API pipeline: Create, configure, start, stop, measure and delete a Telegram campaign of any kind. Groups: API pipeline: Submit your Telegram groups, find groups by keyword, place or rubric, vet them and attach them to a campaign. A free 7-day radar for a new project: API pipeline: Receive a free 7-day starter radar for your project and watch its matches
How it works
Words → meaning → contact.
- Words. Every message heard in your groups is normalised (lowercase, punctuation stripped, single spaces) and matched against
keyword_groups: any keyword of a group (OR), every group (AND), as substrings. Any keyword innot_groupsdrops the message. Hits become post cards in the radar's Matches (kept 90 days; repeated identical texts are stored once). - Meaning (auto only). Once 5+ matches are marked canonical, each new match is compared by meaning with Canonical and with Evicted: it goes to auto contact only when it is close enough to Canonical and closer to it than to any evicted match. Matches are stored either way.
- Contact. The author (never a bot) is written by one of the radar's oracles under its agenda, once per person per project. The conversation lands in your inbox as a room.
Steps
1. Create (Paused).
POST /v0/my/campaigns
Idempotency-Key: radar-nails-la-1
{"title": "Nail masters LA", "project_nonce": "<Pro project>",
"keyword_groups": [["manicure", "pedicure", "nails"], ["looking for", "need", "recommend"]],
"not_groups": [["vacancy", "hiring"]],
"tg_groups": ["<preset nonce>"]}
→ {"nonce": "470370000000000001", "card_type": "campaign", "kind": "mtproto_radar",
"status": "Paused", "is_outreach_enabled": false, "is_auto_enabled": false,
"keyword_groups": […], "not_groups": […], "telegram_groups_list": ["<preset nonce>"], …}
kind defaults to mtproto_radar. The response also carries fields of other kinds (speed, reactions, batch_*); ignore them.
2. Start.
POST /v0/my/campaigns/470370000000000001/start
→ {"success": true, "status": "Active", "is_active": true}
The first start creates the radar's workspaces and shares them with you: Matches (read), Canonical (read + write); GET /v0/my/campaigns/{nonce} then shows matches_workspace and canonical_matches_workspace (and evicted_matches_workspace after the first evict). Typical 400s: project needs an active Pro membership to run campaigns, outreach requires oracles assigned, outreach requires an agenda assigned.
3. Read matches, newest first.
GET /v0/my/workspaces/<matches_workspace>?contained_limit=200&contained_offset=0
→ {"contained": [{"card_type": "post", "nonce": "470370000000000101", "snippet": "…",
"source_url": "https://t.me/example_group/17552", "created_at": "…", …}],
"contained_next_offset": 200, …}
One match in full (author, group): GET /v0/graph/posts/{nonce} → author_username, source_group, source_group_username, expires_at. Search inside matches: POST /v0/search/graph/keyword {"query": "gel", "card_type": "post", "workspace": "<matches_workspace>"}.
4. Tune.
- Mark good matches canonical:
POST /v0/my/workspaces/<canonical_matches_workspace>/<post>; unmark:DELETEon the same path. - Move bad matches out (they become negatives for meaning):
POST /v0/my/campaigns/{nonce}/matches/evict {"posts": ["<post>", …]}→{"evicted": 2, "evicted_matches_workspace": "…"}. Before the first start: 400radar has no matches surface yet. - New excludes, then clean the backlog:
PATCH {"not_groups": [[…]]}, thenPOST …/matches/sweep {"dry_run": true}→{"matched": 46, "evicted": 0}; repeat with{"dry_run": false}(an empty body also evicts). Sweep looks at the newest matches. - An Active radar picks up keyword, exclude, group and auto changes while it runs. Canonical and Evicted changes reach the meaning check at once.
5. First contact (optional). Turn outreach on: PATCH {"oracles": ["<oracle or team workspace>"], "agenda": "<swappable agenda>", "is_outreach_enabled": true}.
One match at a time (owner only):
POST /v0/my/campaigns/{nonce}/matches/{post}/draft
{"instructions": "mention the free first consultation"}
→ {"text": "…", "target_username": "…", "oracle_nonce": "…", "model": "…"}
POST /v0/my/campaigns/{nonce}/matches/{post}/outreach
{"text": "<the text you approved>"}
→ {"ok": true}
Each can take up to a couple of minutes; if one fails, try again. Typical 400s: outreach is not enabled on this radar, no oracle available: …, all candidates already outreached for this project, no human outreach candidate in post.
Automatic: mark 5+ canonical, then PATCH {"is_auto_enabled": true}.
6. Follow results. Conversations are rooms in your inbox: API pipeline: Supervise conversations your oracles hold: turn the listener off/on, clear a handoff, set a room's agenda, ask the oracle to continue. performance and sends count group posts, so they stay at zero for a radar.
7. Stop, delete, share. POST …/stop pauses (config and matches kept); stop before DELETE. Share it by a link (index, «Sharing by links»): r lets the claimer read config and status; w also edit keywords and start / stop. Matches, Canonical and Evicted are workspaces Cabi.Chat shares with you: your link does not reach them.
8. Where your matches come from. The groups your radar hears matches in, ranked: most matches first, then most distinct authors.
GET /v0/my/campaigns/{nonce}/source_groups?days=14&limit=50
→ {"campaign": "470370000000000001", "days": 14,
"groups": [{"nonce": "…", "handle": "tgg.example_group", "title": "…", "members": 13934,
"matches": 41, "authors": 29, "last_seen": "…"}, …]}
days: the period counted back from now (default 14);limit: how many groups (default 50).- You need read on the radar's Matches, which you have as its owner.
- Keep the ranking as a workspace:
POST /v0/my/campaigns/{nonce}/source_groups/workspace {"days": 14, "limit": 50}→{"workspace": "…", "title": "<radar title> - target groups", "created": true, "groups": […]}. A Private workspace of yours, in rank order; calling it again refreshes the same workspace. With your own session or token only. - Why: the groups where people already post what your radar listens for are where such posts live. Put that workspace into an announcement campaign's
tg_groupsto post where your kind of posts already are: API pipeline: Post your posts into groups at a pace set by their traffic and follow what was sent and deleted
Rules
- Keywords match as stored. Write them lowercase, without punctuation (
#,:,-,+), plain letters, single spaces:"where can", not"where+can". - Empty
keyword_groupsnever matches. Pick the groups to listen in withtg_groups(groups or presets). - Pro. Start needs
project_nonceof a Pro project. - Outreach needs
oraclesand a swappableagenda; without them keep it off: the radar still collects matches. - Auto needs outreach on and 5+ canonical matches; not allowed at create (400
enable auto outreach after marking at least 5 canonical matches). To switch outreach off, switch auto off in the same PATCH or before (400auto outreach requires outreach enabled). - Every first message (draft or auto) is an AI run; its cost shows in your AI spend: API pipeline: See your AI spend. Prices: link
API surface
| Endpoint | Purpose | FE | AI / oracle |
|---|---|---|---|
POST /v0/my/campaigns |
Create the radar | yes | yes |
GET /v0/my/campaigns, GET /v0/my/campaigns/{nonce} |
List; config, status, workspaces | yes | yes |
PATCH /v0/my/campaigns/{nonce} |
Keywords, excludes, groups, oracles, agenda, outreach, auto | yes | yes |
POST /v0/my/campaigns/{nonce}/start, /stop |
Run / pause | yes | yes |
GET /v0/my/workspaces/{matches_workspace} |
Read matches | yes | yes |
GET /v0/graph/posts/{nonce} |
Match details: author, group | yes | yes |
POST /v0/search/graph/keyword with workspace |
Search inside matches | yes | yes |
POST / DELETE /v0/my/workspaces/{canonical}/{post} |
Mark / unmark canonical | yes | yes |
POST /v0/my/campaigns/{nonce}/matches/evict |
Evict matches (negatives) | yes | yes |
POST /v0/my/campaigns/{nonce}/matches/sweep |
Evict what the excludes now drop | yes | yes |
POST /v0/my/campaigns/{nonce}/matches/{post}/draft |
Oracle drafts a first message | yes | user's own AI |
POST /v0/my/campaigns/{nonce}/matches/{post}/outreach |
Send the approved text | yes | user's own AI |
DELETE /v0/my/campaigns/{nonce} |
Delete (stop first) | yes | rarely |
POST /v0/my/links, GET /v0/my/links/{link}/grants |
Share the radar; who holds it | yes | yes |
GET /v0/my/campaigns/{nonce}/source_groups |
Groups the matches come from, ranked | no | yes |
POST /v0/my/campaigns/{nonce}/source_groups/workspace |
Keep that ranking as a workspace | no | your token |