Target Live Updates
Subscribe to the targets channel to mirror the lifecycle of your subscription bot targets — Telegram groups, Discord servers, channels, and roles — live in an admin UI. The channel emits events when targets are added, removed, renamed, enabled, or disabled.
Subscribing
No identifier is required. Subscribing returns every target event for the project the connection is authenticated against.
{ "action": "subscribe", "channel": "targets" }The server replies with subscribed. Use action: "unsubscribe" with the same channel to stop the feed.
Events
All events arrive as named Socket.IO events with snake_case field names and the standard broadcast envelope (event_id, emitted_at).
Common fields
Every event on this channel carries these top-level fields:
| Field | Type | Notes |
|---|---|---|
channel | string | Always "targets". |
project_id | string | The owning project. |
platform | string | "discord" or "telegram". |
Events that carry a target object include the full snapshot below. Treat it as a complete state replacement, not a diff:
target field | Type | Notes |
|---|---|---|
target_id | UUID | BotSubscription identifier. |
target_name | string | Human-readable name (e.g. server or group name). |
platform | string | "discord" or "telegram". |
kind | string | guild, group, channel, or role. |
external_id | string | The platform's own ID (Discord guild ID, Telegram chat ID, etc.). |
is_enabled | boolean | Whether the target is currently active. |
parent_target_id | UUID | null | For nested targets (e.g. a channel under a guild). |
plan_count | integer (optional) | Number of plans attached. May be absent for some events. |
participant_count | integer (optional) | Number of active members. May be absent for some events. |
target.added
A new target was created. Only fires for newly persisted targets — not cache rehydration.
{
"event_id": "9b724ac8-f0e1-4b56-8d7a-2c9c0d11b2f1",
"emitted_at": 1779836400000,
"channel": "targets",
"project_id": "93425026-6bb8-4f81-a75d-63f538e1a123",
"platform": "discord",
"target": {
"target_id": "1f2e3d4c-5b6a-7e8f-9a0b-1c2d3e4f5a6b",
"target_name": "My Server",
"platform": "discord",
"kind": "guild",
"external_id": "123456789012345678",
"is_enabled": true,
"parent_target_id": null,
"plan_count": 0,
"participant_count": 0
}
}target.removed
A target was deleted. The payload identifies the target without resending the full record.
{
"event_id": "5e2a1b0d-7c61-4d83-9f10-aa00b2c3d4e5",
"emitted_at": 1779836400000,
"channel": "targets",
"project_id": "93425026-6bb8-4f81-a75d-63f538e1a123",
"platform": "telegram",
"target_id": "1f2e3d4c-5b6a-7e8f-9a0b-1c2d3e4f5a6b",
"kind": "channel",
"external_id": "-1001234567890"
}target.enabled / target.disabled
Fires when is_enabled changes value. Both events carry the full updated target snapshot.
{
"event_id": "uuid",
"emitted_at": 1779836400000,
"channel": "targets",
"project_id": "93425026-6bb8-4f81-a75d-63f538e1a123",
"platform": "discord",
"target": {
"target_id": "1f2e3d4c-5b6a-7e8f-9a0b-1c2d3e4f5a6b",
"target_name": "My Server",
"platform": "discord",
"kind": "guild",
"external_id": "123456789012345678",
"is_enabled": false,
"parent_target_id": null
}
}No-op updates are suppressed. Setting is_enabled: true on an already-enabled target produces no event. Only real false → true and true → false transitions fire.
target.updated
Fires when target metadata changes without an is_enabled transition. Triggered by changes to target_name, kind, external_id, parent_target_id, plan_count, or participant_count.
{
"event_id": "uuid",
"emitted_at": 1779836400000,
"channel": "targets",
"project_id": "93425026-6bb8-4f81-a75d-63f538e1a123",
"platform": "discord",
"target": {
"target_id": "1f2e3d4c-5b6a-7e8f-9a0b-1c2d3e4f5a6b",
"target_name": "My Server (renamed)",
"platform": "discord",
"kind": "guild",
"external_id": "123456789012345678",
"is_enabled": true,
"parent_target_id": null,
"plan_count": 3,
"participant_count": 142
}
}The target field carries the full new state — overwrite your local record from it instead of computing a diff.
Combined updates. When a single backend update changes is_enabled and other metadata in the same call, only target.enabled or target.disabled fires (with the new metadata already reflected in the target snapshot). target.updated is not emitted alongside. A client that overwrites local state from the target field on every event handles all three cases uniformly.
Recommended client pattern
- On
ready, subscribe totargets. - Keep a local map keyed by
target_id. - On
target.added,target.enabled,target.disabled, andtarget.updated, overwrite the entry from thetargetpayload. - On
target.removed, delete the entry usingtarget_id. - After a reconnect, resubscribe and reconcile with a fresh REST fetch if you need authoritative counts.
Next steps
- Track a single payment in Payment Request live updates.
- Reflect saved cards and wallets in Payment Method live updates.
- Manage targets via REST in the Targets API.