Zapnoty — Broadcast — mass delivery

API Documentation

REST API for notifications via Telegram and Max. Subscribers, OTP, broadcasts, forms, helpdesk.

Broadcast (mass delivery)

Send a message to all subscribers or a segment.

POST /v1/broadcast

Creates a broadcast job. Messages are sent through a queue.

text string required

Message text

format string

Text format: plain (default), markdown, or html

media object

Media object: {type, url}. Types: photo, video, document

buttons array

Array of button rows: [[{text, url}]] or [[{text, callback_data}]]

permission string

Filter by permission

tags array

Filter by tags: ["vip", "beta"]

tags_all array

AND filter: subscriber must have ALL listed tags

tags_any array

OR filter: subscriber must have at least one of the tags

exclude_tags array

Exclude subscribers with any of these tags

exclude_permissions array

Exclude subscribers with any of these permissions

messages_per_minute number

Rate limit (messages/min). Defaults to max bot quota

dry_run boolean

true — preview without sending: returns audience, credits and a sample message

POST /v1/broadcast
 
{
"text": "Version 2.0 available!",
"permission": "updates",
"tags": ["beta"]
}

Response

{
"job_id": "a1b2c3d4-e5f6-...",
"status": "pending",
"total_subscribers": 1500
}

Preview (dry-run)

With dry_run: true no broadcast is created — you get an audience/credit estimate and a sample rendered message. Handy to verify filters before sending.

POST /v1/broadcast
 
{
"text": "20% off for VIP",
"tags_all": ["vip"],
"exclude_tags": ["unsubscribed_promo"],
"dry_run": true
}

Response

{
"dry_run": true,
"would_send_to": 342,
"estimated_credits": 342,
"rendered_sample": "20% off for VIP",
"filters_applied": { ... }
}

GET /v1/broadcast/:job_id

Get broadcast status.

Response fields: status (pending/processing/completed/failed/cancelled), total, sent, failed, skipped.

{
"job_id": "b7f3...",
"status": "completed",
"total": 2847,
"sent": 2835,
"failed": 12,
"skipped": 0
}

DELETE /v1/broadcast/:job_id

Stops a broadcast. Already sent messages stay, no new ones go out. The worker notices the cancellation on its next iteration; on finish it fires the broadcast.cancelled webhook.

GET /v1/broadcast/:job_id/deliveries

Detailed per-subscriber delivery report. Params: status (success/failed), limit (up to 500), cursor (for pagination — the next_cursor value from the previous response).

GET /v1/broadcast/{job_id}/deliveries?status=failed
 
{
"deliveries": [
{ "channel": "telegram", "chat_id": 123, "status": "failed", "error": "bot blocked" }
],
"next_cursor": "2026-05-20T10:00:00Z"
}

Frequency cap

Project-level cap on marketing messages per subscriber (per day / per week). Configured in the dashboard on the Broadcasts tab. Off by default. Subscribers over the limit are skipped during a broadcast (skipped field in the status).

Related sections