API Documentation

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

Guides

Step-by-step recipes for common tasks. Each guide is a self-contained walkthrough from the first request to the result.

Integrate in 5 minutes

Create a project in the dashboard, connect a Telegram bot and copy the API key (zn_live_...). The bot reaches subscribers after /start — each /start creates a subscription. Get a subscriber_id via GET /v1/subscribers and send your first message:

curl -X POST https://api.zapnoty.com/v1/send \
-H "Authorization: Bearer zn_live_..." \
-H "Content-Type: application/json" \
-d '{"subscriber_id":"550e8400-...","text":"Hello!"}'

Done — the message is delivered. Next: templates, media, buttons — see the "Send" section.

OTP login for users

Replace SMS codes with free delivery via Telegram/Max. Zapnoty generates the code, sends it to the subscriber and verifies the input. The purpose field separates codes for different scenarios (login, payment).

POST /v1/otp/send { "subscriber_id": "...", "purpose": "login" }
POST /v1/otp/verify { "subscriber_id": "...", "code": "123456", "purpose": "login" }
→ { "verified": true }

Limit — 3 codes per hour per subscriber. Code length (4/6/8) and TTL are configurable. For users not yet subscribed there is the /v1/otp/request deeplink flow.

Broadcasts and segmentation

A broadcast filters the audience by tags and permissions. tags_all — all tags (AND), tags_any — at least one (OR), exclude_tags — exclusions. Before sending, add dry_run: true to see the audience size and credit estimate.

POST /v1/broadcast
{
"text": "News for VIP",
"tags_all": ["vip"],
"exclude_tags": ["unsubscribed_promo"],
"permission": "marketing"
}

Status — GET /v1/broadcast/{job_id}, cancel — DELETE. messages_per_minute spreads a large broadcast over time.

Drip automation and events

A drip chain sends a series of messages on a trigger. The event trigger starts the chain when /v1/events/track is called — a Customer.io alternative: track user actions (signup, lesson_started, cart_updated) and build automation on them. cancel_on_events stops the chain (a purchase cancels an abandoned cart).

POST /v1/drip-chains
{
"name": "Onboarding",
"trigger": "event",
"trigger_value": "signup",
"steps": [
{ "delay_minutes": 0, "text": "Welcome" },
{ "delay_minutes": 1440, "text": "Tip of the day" }
]
}
POST /v1/events/track { "subscriber_id": "...", "event": "signup" }

Event properties are available in the template as {{event.key}}. properties_match filters the trigger by properties.

Receiving webhooks

Webhooks notify your server about events (delivery, unsubscribe, ticket reply). Create an endpoint via /v1/webhooks with a URL and events. Every request is signed with the X-Zapnoty-Signature header (HMAC-SHA256 of "timestamp.body") — always verify the signature:

// Verify X-Zapnoty-Signature (Node)
import crypto from 'crypto';
// header: "t=<unix_ts>,v1=<hex>[,v1=<hex>]"
function verify(secret, header, body) {
const parts = header.split(',');
const ts = parts.find(p => p.startsWith('t='))?.slice(2);
const expected = crypto.createHmac('sha256', secret)
.update(ts + '.' + body).digest('hex');
return parts.some(p => p === 'v1=' + expected);
}

Failed deliveries are retried for up to 24 hours on a Stripe-style schedule. The delivery log is in the dashboard. See the "Webhooks" section for details.

Related sections