Active monitors
Unlike the heartbeat, where your service “calls in” itself, an active monitor works the other way around: a Notifly serverless function polls the specified resource every N seconds. If the specified number of consecutive attempts fail — an alert is sent; when connectivity is restored — a recovery is sent.
There are 11 supported check types — from a simple HTTP-GET to a lightweight
protocol handshake for SMTP / IMAP / POP3 / SSH / Redis / Postgres / MySQL,
DNS resolution and TLS certificate expiry checks:
kind | What it checks | Format of target |
|---|---|---|
http | GET <url>, expect 2xx (or expectedStatus) | https://example.com/health |
tcp | TCP handshake — port is open | host:port |
tls | TCP+TLS handshake; expectedStatus = minimum certificate validity threshold (days) | host:port |
smtp | TCP, read 220 banner, send EHLO/QUIT | mx.example.com:25 |
imap | TCP, expect * OK ..., send LOGOUT | imap.example.com:143 |
pop3 | TCP, expect +OK ..., send QUIT | pop3.example.com:110 |
ssh | TCP, read SSH-2.0-... banner | host:22 |
redis | TCP, send PING\r\n, expect +PONG | redis.example.com:6379 |
postgres | TCP, send StartupMessage, expect R/E/N | db.example.com:5432 |
mysql | TCP, read handshake packet (protocol_version=10) | db.example.com:3306 |
dns | Resolve the name using the system or specified resolver | example.com or example.com@8.8.8.8:53 |
All TLS variants are performed over plain TCP (without StartTLS), so for encrypted ports specify the already-encrypted port (
smtp→:465,imap→:993,pop3→:995,redis→:6380, etc.). To check raw TCP connectivity — usekind: "tcp".
How it works
Section titled “How it works”every minute: timer-trigger → notifly-monitor └─▶ SELECT WHERE next_check_at <= now AND status != "paused" └─▶ HTTP GET / TCP Dial with timeoutSec ├─ success → status=up, fail_count=0 │ if was "down" and recoveryMessage exists → recovery └─ error → fail_count++; if ≥ consecutiveFails and not "down": send alert, status=down- Storage — the
monitorstable in YDB Serverless, indexed bynext_check_at. - Check — a separate Cloud Function
notifly-monitor, invoked by a Yandex Cloud timer- trigger (cron* * * * ? *— once per minute). - Notification — a regular Notifly message, sent via the channel chosen when creating the monitor and delivered to all your clients (web, Android, desktop), like any other push notification.
Events
Section titled “Events”Since Notifly initiates the communication, there are two explicit events:
| Event | When it’s sent | Text |
|---|---|---|
| Loss of connectivity (alert) | After consecutiveFails consecutive failures, only on the first transition to down. The message includes the technical error (e.g., dial tcp: connect: connection refused) | alertMessage |
| Recovery (recovery) | On the first successful check after a down state, only if recoveryMessage is set | recoveryMessage |
There will be no repeated alert spam: while the monitor is down, no new messages are sent,
checks continue at the regular interval and only record the status.
Creating a monitor
Section titled “Creating a monitor”Via the admin UI
Section titled “Via the admin UI”- Open app.notifly.ru → Monitors.
- Click Create monitor, fill in:
- Name — for display, e.g. “Prod site” or “Postgres replica”.
- Channel — which channel to send the notification through.
- Check type — one of the 11 supported
kindvalues (see table above). - Target — format depends on the type: URL,
host:portor DNS name. - Interval (sec) — between checks (minimum 30, maximum 86400).
- Timeout (sec) — how long to wait for a response in one attempt (1–60, default 10).
- Consecutive fails — number of consecutive failed checks before an alert (default 1; typically 2–3 to avoid reacting to single network jitters).
- Expected HTTP status (only
http) —0means “any 2xx”, otherwise an explicit code (e.g.,200). - Certificate buffer, days (only
tls) —0= do not check;30= alert 30 days before expiry. - Alert / recovery text — what will appear in the push.
Via REST API
Section titled “Via REST API”# HTTP-проверкаcurl -X POST "$NOTIFLY_URL/monitor" \ -H "Content-Type: application/json" \ -H "X-Gotify-Key: <client-token>" \ -d '{ "appid": 12345, "name": "Прод-сайт", "kind": "http", "target": "https://example.com/health", "intervalSec": 60, "timeoutSec": 10, "expectedStatus": 0, "consecutiveFails": 2, "alertMessage": "Сайт недоступен!", "alertPriority": 9, "recoveryMessage": "Сайт снова отвечает." }'
# TCP-проверкаcurl -X POST "$NOTIFLY_URL/monitor" \ -H "Content-Type: application/json" \ -H "X-Gotify-Key: <client-token>" \ -d '{ "appid": 12345, "name": "Postgres-реплика", "kind": "tcp", "target": "db-replica.internal:5432", "intervalSec": 60, "timeoutSec": 5, "consecutiveFails": 3, "alertMessage": "Реплика не принимает соединения.", "recoveryMessage": "Реплика снова доступна." }'
# TLS — алёрт за 30 дней до истечения сертификатаcurl -X POST "$NOTIFLY_URL/monitor" \ -H "Content-Type: application/json" \ -H "X-Gotify-Key: <client-token>" \ -d '{ "appid": 12345, "name": "SSL prod", "kind": "tls", "target": "example.com:443", "intervalSec": 3600, "timeoutSec": 10, "expectedStatus": 30, "alertMessage": "Сертификат example.com истекает менее чем через 30 дней!" }'
# DNS — проверка резолва через указанный серверcurl -X POST "$NOTIFLY_URL/monitor" \ -H "Content-Type: application/json" \ -H "X-Gotify-Key: <client-token>" \ -d '{ "appid": 12345, "name": "DNS prod-зоны", "kind": "dns", "target": "example.com@8.8.8.8:53", "intervalSec": 300, "timeoutSec": 5, "alertMessage": "DNS-зона example.com не резолвится через 8.8.8.8" }'Examples by type
Section titled “Examples by type”Typical kind + target combinations for common tasks:
{ "kind": "smtp", "target": "mx.example.com:25" } // SMTP-MX responds{ "kind": "imap", "target": "imap.example.com:143" } // IMAP is alive (plain){ "kind": "pop3", "target": "pop3.example.com:110" } // POP3 is alive{ "kind": "ssh", "target": "jump.example.com:22" } // SSH banner{ "kind": "redis", "target": "redis.example.com:6379"} // Redis responds to PING{ "kind": "postgres", "target": "pg.example.com:5432" } // PostgreSQL accepts connections{ "kind": "mysql", "target": "db.example.com:3306" } // MySQL sends handshake{ "kind": "tls", "target": "smtps.example.com:465", "expectedStatus": 14 } // SSL certificate is valid and will not expire within the next 14 daysVia MCP (for AI assistant)
Section titled “Via MCP (for AI assistant)”If you have a Notifly MCP server configured (see MCP), just ask:
Create a monitor for https://api.example.com/health, check every 30 seconds, alert after 3 consecutive failures with the text “API is down”, recovery “API is working again”.
Available MCP tools: list_monitors, create_monitor, update_monitor,
delete_monitor, pause_monitor, resume_monitor.
REST endpoints
Section titled “REST endpoints”| Method | Path | Description |
|---|---|---|
GET | /monitor | List monitors for the user |
POST | /monitor | Create |
PUT | /monitor/:id | Update settings (cannot change appid) |
DELETE | /monitor/:id | Delete |
POST | /monitor/:id/pause | Pause (checks are not performed) |
POST | /monitor/:id/resume | Resume |
All endpoints, except pause/resume, require a client (or MCP) token with write permission.
Why YDB, not S3
Section titled “Why YDB, not S3”See the same reasoning for heartbeat: an indexed
point query by next_check_at is cheaper and faster than listing S3 objects.
Limitations
Section titled “Limitations”- Minimum interval: 30 seconds (to avoid overloading YDB).
- Maximum interval: 24 hours (for infrequent checks it’s better to use heartbeat).
- One invocation of
notifly-monitorprocesses at most 200 overdue monitors (cron runs once per minute; checks are performed sequentially). - Requests originate from Yandex Cloud, so the target resource must be reachable from the public internet (or from a private YC network if the function is in the appropriate VPC).
- All protocol-specific checks (
smtp/imap/pop3/…) are performed without StartTLS — for the encrypted variant use the already-encrypted port (:465,:993,:995,:6380) withkind: "tls"orkind: "tcp".