API Documentation
Quick start
Three steps to your first notification.
1. Create a project
Sign up with Yandex or VK, create a project in the dashboard.
2. Get an API key
Copy your API key (format: zn_aBcDeFgH...) from project settings.
3. Send your first request
Use curl or any HTTP client:
Authentication
All API requests require a Bearer token in the Authorization header.
Base URL: https://api.zapnoty.comContent-Type: application/json required for all POST/PUT/PATCH requestsHeader format:
API key format: zn_ + 32 characters. Keys are created in the project dashboard.
Never pass the key in URL or query parameters. Use only the Authorization header.
Bot Auth
Authenticate your website users via Telegram/Max bots. User clicks a link, confirms in the bot, automatically subscribes to notifications, and you receive their data.
How it works
1. User clicks the bot link
2. Bot shows authorization request with "Login" / "Cancel" buttons
3. User clicks "Login" → automatic subscription to notifications + code generation
4. Bot sends "Go to website" button → callback_url?code=CODE
5. Your server calls POST /v1/auth/verify → receives data
Setup
In the project dashboard go to Settings → Auth and set the Callback URL and Origin URL.
Static links
Simple option — a permanent link. Get it via API or in the dashboard:
API sessions (with state)
To pass state (e.g., browser session ID), create a session via API:
Code verification
After confirmation, the user lands on callback_url?code=CODE. Verify the code:
QR/Polling (cross-device)
For desktop auth via a mobile bot: create a session, display a QR code with the link, and poll the status. When the user confirms on their phone, you receive a code. Poll interval: 2-3 seconds. The code is one-time — after receiving status=completed, a repeat request returns expired.
POST /v1/send
Send a personal notification to a specific subscriber.
Parameters
subscriber_id string (UUID) Subscriber UUID (from subscriber list)
text string Message text (up to 4000 characters). Required if template is not specified
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}]]
template string Template slug instead of text
vars object Template variables: {key: value}
permission string Filter by permission: send only to subscribers with this key
segment string Filter by segment (tag)
Request example
Response
OTP (one-time passwords)
Send and verify confirmation codes via messenger.
POST /v1/otp/send
Generates a 6-digit code and sends it to the subscriber.
subscriber_id string (UUID) requiredSubscriber UUID
Response
POST /v1/otp/verify
Verifies the entered code.
subscriber_id string (UUID) requiredSubscriber UUID
code string required6-digit code from the user
Response
✓ Code valid
✗ Code invalid
OTP limits: max 5 verification attempts, 5-minute code TTL, 1 active code per subscriber.
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 requiredMessage 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"]
Response
GET /v1/broadcast/:job_id
Get broadcast status.
Response fields: status (pending/processing/completed/failed), total, sent, failed.
Subscribers
Manage subscriber list and their tags.
GET /v1/subscribers
Project subscriber list. Pagination via query parameters.
limit number Number of records (default 50, max 200)
offset number Offset from the beginning of the list (default 0)
channel string Filter by channel: telegram or max
Response
PUT /v1/subscribers/:id/tags
Update subscriber tags. Pass the full array of tags. Tags must be pre-created in project settings or via API. Unknown tags will be auto-created (if the 20-tag limit is not reached).
Response
Templates
Templates let you reuse text with variables. Created in the dashboard or via API.
Usage: pass template and vars instead of text in /v1/send.
Variables in templates use {{name}} syntax. Example: "Order {{order_id}} delivered".
Example
Media & buttons
Attach media files and inline buttons to notifications.
Media types: photo, video, document. Pass the file URL.
Buttons are a 2D array: outer array = rows, inner array = buttons in a row.
- URL button: {"text": "Open", "url": "https://..."}
- Callback button: {"text": "Yes", "callback_data": "confirm_123"}
Example with media and buttons
Limitations
1 media + buttons — supported. Multiple media + buttons — not supported (Telegram limitation). Caption with media: up to 1024 characters (Telegram) / up to 4000 (Max). Text without media — up to 4000 characters.
Management API
API for managing templates, permissions, tags, and webhook. All endpoints require a Bearer token.
Templates
GET /v1/templates — list templatesPOST /v1/templates — create template (key, text, format?)PUT /v1/templates/{key} — update template (text?, format?)DELETE /v1/templates/{key} — delete templatePermissions
GET /v1/permissions — list permissionsPOST /v1/permissions — create (key, title, description?, required?)PUT /v1/permissions/{key} — update (title?, description?, required?)Deleting permissions is only available in the dashboard.
Tags
GET /v1/tags — list project tagsPOST /v1/tags — create tag (name)PUT /v1/tags/{name} — rename tag (new_name)DELETE /v1/tags/{name} — delete tag (also removed from all subscribers)Allowed characters: letters, numbers, - and _. Maximum 20 tags.
Sender signature
Each message can include a signature — sender name and description. Configured in the dashboard (Overview → Notifications or Settings → Notifications).
Signature format: text\n\n— Name\nDescription
Logo: uploaded via dashboard, resized to 256×256 PNG.
Auto-messages
Manage auto-messages on subscribe/unsubscribe via API. Each message is toggled independently and supports two languages (RU/EN). Project signature is appended automatically.
GET /v1/auto-messages — get settingsPUT /v1/auto-messages — update settingsFields: subscribe_message_enabled, unsubscribe_message_enabled (boolean), subscribe_message, unsubscribe_message ({"ru":"...","en":"..."}). When disabled, default text is sent.
OTP template
Custom OTP message template. Must contain {{code}}. You can also use {{minutes}} for expiry time.
Example: Your verification code: {{code}}. Valid for {{minutes}} min.
Webhook
GET /v1/webhook — get current webhookPUT /v1/webhook — set webhook (url, events?[])DELETE /v1/webhook — delete webhookIf events is not specified — all events will be sent. Secret is generated automatically.
Webhooks
Zapnoty sends HTTP POST to your URL when events occur. One webhook per project. Configured in the dashboard or via API.
Events: subscription.created, subscription.deleted, delivery.success, delivery.failed, broadcast.completed, button.clicked, auth.completed, ticket.created, ticket.replied, ticket.status_changed, ticket.assigned, ticket.closed.
Signature: X-Zapnoty-Signature header contains HMAC-SHA256 of request body with your webhook secret.
Signature verification:
Payload format:
Retry policy
On delivery failure, webhooks are automatically retried up to 3 times with exponential backoff (1s → 2s → 4s). Each attempt has a 10-second timeout. 4xx responses from your server are not retried. We recommend implementing idempotent processing on your side.
Scheduled Messages
Delayed messages, drip chains, and recurring broadcasts
Scheduled Send
Send a message at a specified time. Supports formatting (html/markdown), media attachments, and inline buttons.
subscriber_id string (UUID) requiredSubscriber UUID
text string requiredMessage text
scheduled_at string (ISO 8601) requiredSend date and time (ISO 8601 UTC)
name string Name (for dashboard)
Drip Chains
Automatic sequence of messages on subscription or event. Each step supports formatting, media, and buttons.
name string requiredName (for dashboard)
trigger string requiredTrigger: subscription, segment, permission
trigger_value string Trigger value (tag or permission key)
steps[].text string requiredMessage text
steps[].delay_minutes number requiredDelay from trigger moment (minutes)
Recurring
Regular broadcast at a set interval. Supports formatting, media, buttons, and filters (channel, segment, permission).
name string requiredName (for dashboard)
text string requiredMessage text
interval_hours number requiredRepeat interval (hours)
Helpdesk API
Ticket support system. Create tickets from customers, reply to them and manage statuses. Users are automatically subscribed to notifications when contacting support. Enable Helpdesk in project settings.
POST /v1/helpdesk/tickets — create ticket
Creates a ticket on behalf of a customer. Ticket number is auto-generated.
text string requiredFirst message text
subject string Ticket subject (optional)
channel string Channel: telegram, max or virtual. Defaults to virtual
chat_id number Messenger chat ID. Required for telegram/max
first_name string Customer first name
username string Customer username
ticket_type_id string Ticket type UUID (from GET /v1/helpdesk/ticket-types). Optional.
Response
GET /v1/helpdesk/tickets — list tickets
Returns project tickets. Filter by status: ?status=new|in_progress|waiting|closed.
GET /v1/helpdesk/tickets/{id} — ticket details
Returns full ticket information by ID, including all messages.
POST /v1/helpdesk/tickets/{id}/reply — reply to ticket
Sends an agent reply to the ticket. Customer will receive a notification in messenger.
POST /v1/helpdesk/tickets/{id}/customer-reply — virtual customer reply
Sends a reply on behalf of a virtual customer (channel=virtual). Only for tickets with virtual channel. Agents will be notified and a ticket.replied webhook will be dispatched.
text string requiredFirst message text
PATCH /v1/helpdesk/tickets/{id}/status — change status
Changes ticket status. Allowed values: open, in_progress, closed.
PATCH /v1/helpdesk/tickets/{id}/assign — assign agent
Assigns the ticket to a support agent.
user_id string requiredAgent user ID
PATCH /v1/helpdesk/tickets/{id}/priority — ticket priority
Changes ticket priority. Allowed values: low, normal, high, urgent.
priority string requiredPriority: low, normal, high, urgent
GET /v1/helpdesk/ticket-types — ticket types
Returns list of active ticket types for the project. Each type contains id, name, description. Use ticket_type_id when creating a ticket to classify requests. Types are configured in the dashboard (Settings → Support).
Webhook events
ticket.created — ticket created, ticket.replied — ticket replied, ticket.status_changed — status changed, ticket.assigned — agent assigned, ticket.closed — ticket closed.
Forms
Public endpoint for receiving form submissions from websites. No API key required — safe for client-side JavaScript.
POST /f/{form_id}
body object requiredArbitrary JSON — any form fields
Request example
Response
Form Fields
Any fields are accepted. Standard fields are highlighted in notifications and will be used in future integrations (CRM, Google Sheets).
name string Sender name
email string Sender email
phone string Phone number
subject string Subject
company string Company
city string City
url string Website URL
message string Message text
* any Any other fields — passed as-is
allowed_origins format
Allowed origins are specified as domains. Scheme (http/https), www and slashes are normalized automatically — just write example.com and all variants (https://example.com, www.example.com, example.com/) will be accepted.
example.com — accepts submissions from https://example.com, http://example.com, https://www.example.com
*.example.com — any subdomain and example.com itself (requires at least second level, *.com is forbidden)
Local hosts (localhost, 127.0.0.1), private IPs and reserved TLDs (.local, .test, .example) are forbidden — the server returns an error on save
Security
Origin check — only allowed domains
Rate limit — 30 submissions/min per form
Honeypot — hidden _honey field to protect from bots
Sanitization — automatic HTML tag and dangerous content removal
Time-to-submit — submissions faster than 3s are rejected (configurable)
Blocklist of throwaway email services (mailinator, tempmail, …) — flagged as spam
Spam folder & filters
Spam submissions are NOT deleted — they go to a separate list for review. Webhooks and email notifications skip spam.
Spam tab in dashboard — separate list of flagged submissions
Honeypot — hidden field with a unique per-form name. If a bot fills it, the submission is silently rejected
Keyword blocklist — customizable word list. Matches are flagged as spam
Timestamp check — `_submit_time` set on form load, verified on submit
Manually mark submissions as spam/not-spam in the dashboard
Submission Routes (Form Routes)
Unified model: route = trigger + action. Trigger — «always» (default recipient) or «conditional». Actions: deliver to recipient / mark as spam / block. Up to 40 routes per form.
Condition operators: `contains` (substring), `equals` (exact match). Condition field — field name or `*` for any string field
`mark_spam` — save, but flag as spam (no delivery, no webhook)
`block` — don't save at all (silent 200 OK)
`deliver` — deliver to recipient (email/messenger/subscriber/team). Conditional route adds to always-routes by default
Flag `replace_defaults` on a conditional deliver-route replaces all always-routes when matched — use for re-routing (e.g. «only sales submissions → sales@»)
Post-submit redirect
If `redirect_url` is set in form settings and the request came as `application/x-www-form-urlencoded` (HTML form, no JS), the server responds with `303 See Other` and `Location: <redirect_url>`. JSON requests (fetch/AJAX) still get `{ok:true}`.
Recipients: messengers (Telegram/Max), email, project subscribers
Integration example
Limits
Rate limits and field size restrictions.
Rate limit: 300 requests/min per project. Exceeding it returns 429 Too Many Requests.
Message text: up to 4,096 characters.
Buttons: up to 3 rows, up to 3 buttons per row.
Media: up to 20 MB (photo), 50 MB (video/document).
Broadcast: up to 100,000 subscribers per broadcast.
OTP: 5-min TTL, max 5 attempts, 1 active code per subscriber_id.
Tags: up to 20 tags per project, tag length up to 64 characters.
Permissions: up to 20 per project.
Templates: up to 100 per project.
X-RateLimit-* headers are not returned. When the limit is exceeded, the API only returns HTTP 429 with an error body. We recommend implementing exponential backoff on your side.
Error codes
API returns standard HTTP codes with JSON error body. The retryable field indicates whether the request can be retried.
Error format:
400 — Invalid request (missing required fields, wrong format)
401 — Invalid or missing API key
403 — No access to resource
404 — Subscriber or resource not found
409 — Conflict (e.g., OTP already sent)
422 — Validation error (text too long, invalid URL)
429 — Rate limit exceeded (retryable: true, Retry-After header)
500 — Internal server error (retryable: true)
Error handling & retry
Recommendations for handling API errors and retry strategies.
retryable: true — the error is temporary (429, 500). Use exponential backoff: 1s → 2s → 4s, max 3 attempts.
retryable: false — retrying won't help (400, 401, 403, 404, 409). Fix the request before resending.
Retry-After — on 429, this header specifies how many seconds to wait before the next request. Rate limit: 300 requests per minute per project.
Exponential backoff — increase delay between attempts: 1s, 2s, 4s. Do not automatically retry POST requests (send, broadcast) — use the retryable field to make decisions.
Playground
Build an API request and copy the ready curl command.
{
"subscriber_id": "550e8400-e29b-41d4-a716-446655440000",
"text": "Hello from Zapnoty!"
}curl -X POST https://api.zapnoty.com/v1/send \
-H "Authorization: Bearer zn_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"subscriber_id": "550e8400-e29b-41d4-a716-446655440000",
"text": "Hello from Zapnoty!"
}'API vs Dashboard
What you can automate via REST API and what is only available in the dashboard.
Available via API
Send notifications — POST /v1/send (personal, by subscriber_id, with media, buttons, templates)
OTP codes — POST /v1/otp/send and /v1/otp/verify
Mass broadcasts — POST /v1/broadcast, GET /v1/broadcast/:job_id
Subscribers — GET /v1/subscribers, PUT /v1/subscribers/:id/tags
Templates — full CRUD: GET/POST /v1/templates, PUT/DELETE /v1/templates/{key}
Permissions — GET/POST /v1/permissions, PUT /v1/permissions/{key}
Tags — full CRUD: GET/POST /v1/tags, PUT/DELETE /v1/tags/{name}
Webhook — GET/PUT/DELETE /v1/webhook
Bot Auth — GET /v1/auth/link, POST /v1/auth/session, POST /v1/auth/verify, GET session status
Helpdesk — create tickets, reply, change status, assign, set priority, ticket types
Auto-messages — GET/PUT /v1/auto-messages (enable/disable, texts in two languages)
Analytics — GET /api/projects/:id/analytics (via JWT dashboard session)
Dashboard only
Create project — registration and project creation only via UI
API key — generate and regenerate key (requires OTP confirmation)
Delete project — with OTP confirmation via messenger
Sender signature — configure name and description (Settings → Notifications)
Logo — upload and delete project logo
OTP template — multilingual template with HTML formatting and preview
Auth settings — callback URL, origin URL, authorization button text
Enable Helpdesk — activate ticket module in project settings
SLA timers — configure first response and resolution deadlines
Delete permissions — DELETE permissions is only available in the dashboard
QR codes — generate subscription QR codes with styles and logo
API key (Bearer zn_...) is used for all /v1/* endpoints. Dashboard works via JWT session after OAuth (Yandex/VK).