Публичные статус-страницы
Статус-страница — это публичный адрес вида /status/<slug>, на котором любой
человек (клиент, коллега, подписчик) видит, работают ли ваши сервисы. Данные
берутся из уже настроенных мониторов и
heartbeat-ов выбранного канала: ничего дополнительно
настраивать и слать не нужно.
Страница отдаётся сервером как готовый HTML — без авторизации, без JS-фреймворка и без обращения к админке.
Что показывает страница
Заголовок раздела «Что показывает страница»- Общий баннер — «Все системы работают» / «Частичная деградация» / «Сбои в работе сервисов» и средний аптайм за 90 дней.
- Список сервисов — по одной строке на монитор: имя, тип, текущий статус, аптайм за 30 и 90 дней, средний отклик за 30 дней и полоса из 90 дневных ячеек.
- Лента инцидентов — последние 20 авто-инцидентов с длительностью и отметкой «Восстановлено».
- Форма подписки — посетитель оставляет email и получает письмо при каждом инциденте и восстановлении.
посетитель ──GET /status/<slug>────▶ server-rendered HTML (без авторизации) GET /status/<slug>/summary ──▶ тот же срез в JSON (для авто-обновления)
мониторы канала ──переход up↔down──▶ авто-инцидент ──▶ письмо подписчикамЧто попадает на страницу
Заголовок раздела «Что попадает на страницу»Страница привязана к одному каналу. На неё автоматически попадают все источники событий этого канала, которые ведут историю проверок:
| Источник | Тип на странице (kind) |
|---|---|
| Активный монитор (ICMP / TCP / HTTP и т.п.) | собственный вид монитора: icmp, tcp, http, … |
| HTTP-монитор | http |
| Контент-монитор | content |
| Порт-монитор | port |
| Workflow-монитор | workflow |
| Браузерный workflow | browser_workflow |
| Heartbeat | heartbeat |
Статусы и аптайм
Заголовок раздела «Статусы и аптайм»Статус каждого сервиса приводится к единому словарю:
| Статус | Значение |
|---|---|
up | проверки проходят |
degraded | частичная деградация |
down | проверки падают (у heartbeat — пропущен ping) |
pending | проверок ещё не было |
paused | монитор на паузе |
Общий статус страницы собирается по всем сервисам:
- есть хотя бы один
down→down(баннер «Сбои в работе сервисов»); - иначе есть
degraded→degraded; - иначе, если сервисы есть →
ok; - сервисов нет вовсе →
none.
Аптайм считается из тех же дневных бакетов проверок, что и
история мониторов: полоса — 90 дневных ячеек (up,
degraded, down, nodata), рядом — проценты за 30 и 90 дней и средний
отклик за 30 дней. Если данных за день нет, ячейка серая, а процент показывается
как —.
Инциденты
Заголовок раздела «Инциденты»Инциденты создаются автоматически и не требуют ручного ведения:
- монитор перешёл в «падение» → на каждой статус-странице канала открывается
инцидент (
status: "open") с заголовком монитора и текстом последней ошибки; - монитор восстановился → инцидент закрывается (
status: "resolved", проставляетсяresolvedAt).
Операция идемпотентна: повторное падение при уже открытом инциденте ничего не создаёт, а восстановление без открытого инцидента ничего не закрывает. В ленте показываются последние 20 инцидентов.
Создание страницы
Заголовок раздела «Создание страницы»Через админку
Заголовок раздела «Через админку»Раздел Статус-страницы → Создать статус-страницу. Поля диалога:
| Поле | Что задаёт |
|---|---|
| Заголовок | <title> и шапка страницы |
| Slug | адрес: /status/<slug> |
| Канал | чьи мониторы попадут на страницу |
| Описание | подзаголовок под шапкой |
| Язык уведомлений подписчикам | ru или en — язык писем подписчикам и служебных страниц подтверждения/отписки |
| Страница опубликована | выключено = черновик, публичный адрес отвечает 404 |
| Скрыть бейдж «powered by Notifly» | только на тарифах Pro и Business |
Slug нормализуется сервером: нижний регистр, только a-z, 0-9 и дефис;
пробелы и подчёркивания превращаются в дефисы, повторы схлопываются. Например,
My Status Page → my-status-page. Slug уникален глобально: занятый вернёт
409.
Через REST API
Заголовок раздела «Через REST API»curl -X POST "$NOTIFLY_URL/status-page" \ -H "Content-Type: application/json" \ -H "X-Notifly-Key: <client-token>" \ -d '{ "appid": 12345, "slug": "acme-status", "title": "Статус сервисов ACME", "description": "Актуальное состояние API и сайта", "enabled": true, "lang": "ru", "brandingHidden": false }'Ответ — созданный объект страницы:
{ "id": 987, "appid": 12345, "slug": "acme-status", "title": "Статус сервисов ACME", "description": "Актуальное состояние API и сайта", "enabled": true, "brandingHidden": false, "lang": "ru", "created": "2026-06-23T10:00:00Z", "updated": "2026-06-23T10:00:00Z"}Страница сразу доступна по адресу $NOTIFLY_URL/status/acme-status
(например, https://api.notifly.ru/status/acme-status).
Подписка на обновления
Заголовок раздела «Подписка на обновления»Внизу страницы — форма подписки по email. Работает по схеме double opt-in:
- посетитель вводит email →
POST /status/<slug>/subscribe; - на адрес уходит письмо со ссылкой подтверждения
(
GET /status-subscribe/verify?token=…); - после подтверждения подписчик получает письмо при открытии и при закрытии каждого инцидента;
- в каждом письме есть ссылка отписки (
GET /status-subscribe/unsub?token=…).
Повторная подписка тем же адресом не создаёт дубль: подтверждённому подписчику ничего не шлётся, неподтверждённому письмо отправляется заново. Рассылку ведёт тот же минутный воркер доставки, что и эскалацию уведомлений, поэтому письмо приходит в пределах минуты после инцидента.
Письма подписчикам и страницы подтверждения/отписки идут на языке страницы
(поле lang). Интерфейс самой публичной страницы локализуется по заголовку
Accept-Language посетителя (ru/en).
Кэш и авто-обновление
Заголовок раздела «Кэш и авто-обновление»- Агрегат страницы кэшируется на 30 секунд — всплеск трафика не превращается в шторм запросов к базе.
- Открытая страница раз в 45 секунд опрашивает
/status/<slug>/summary. Если общий статус изменился — страница перезагружается, иначе просто обновляется отметка «Обновлено».
JSON для внешних интеграций
Заголовок раздела «JSON для внешних интеграций»GET /status/<slug>/summary отдаёт тот же срез в JSON — удобно для виджета на
своём сайте, дашборда или бота:
curl "$NOTIFLY_URL/status/acme-status/summary"{ "title": "Статус сервисов ACME", "description": "Актуальное состояние API и сайта", "overall": "ok", "overallUptime90d": 99.94, "services": [ { "kind": "http", "id": 555, "name": "API", "status": "up", "uptime30d": 99.98, "uptime90d": 99.94, "avgLatencyMs": 148, "days": [{"date": "2026-03-26", "state": "up", "uptimePct": 100}], "lastCheckAt": "2026-06-23T09:59:12Z" } ], "incidents": [ { "id": 4242, "statusPageId": 987, "appid": 12345, "entityKind": "http_monitor", "entityId": 555, "title": "API", "status": "resolved", "startedAt": "2026-06-21T14:02:00Z", "resolvedAt": "2026-06-21T14:19:00Z", "lastEventAt": "2026-06-21T14:19:00Z", "lastError": "HTTP 502 from https://api.example.com/health" } ], "brandingHidden": false, "generatedAt": "2026-06-23T10:00:00Z"}Проценты аптайма — 0..100; значение -1 означает «нет данных». Endpoint
публичный: авторизация не нужна, отключённая страница (enabled: false) и
неизвестный slug отвечают 404.
Тарифные ограничения
Заголовок раздела «Тарифные ограничения»| Возможность | Free | Pro | Business |
|---|---|---|---|
| Количество статус-страниц | 1 | 5 | Безлимит |
| Скрыть бейдж «powered by Notifly» | — | ✅ | ✅ |
Свой домен (customDomain) | — | — | ✅ |
Превышение лимита страниц вернёт 403 при создании; попытка включить
премиум-поле на неподходящем тарифе — тоже 403 с пояснением в
errorDescription. Актуальные значения лимитов — на странице
Квоты и тарифы.
REST API
Заголовок раздела «REST API»Управление страницами требует клиентский (или MCP) токен и права владельца канала; публичные endpoints работают без авторизации.
| Метод | Путь | Аутентификация | Описание |
|---|---|---|---|
GET | /status-page | client-token | Список своих статус-страниц |
POST | /status-page | client-token (write) | Создать страницу. Body: appid, slug, title, description, enabled, lang, brandingHidden, customDomain |
PUT | /status-page/{id} | client-token (write) | Обновить страницу (тот же payload) |
DELETE | /status-page/{id} | client-token (write) | Удалить страницу |
GET | /status/{slug} | — | Публичная HTML-страница |
GET | /status/{slug}/summary | — | Тот же срез в JSON |
POST | /status/{slug}/subscribe | — | Подписка по email: { "email": "user@example.com" } |
GET | /status-subscribe/verify?token=… | — | Подтверждение подписки (ссылка из письма) |
GET | /status-subscribe/unsub?token=… | — | Отписка (ссылка из письма) |
Коды ответов CRUD: 400 — некорректный slug/язык или чужой канал, 403 —
лимит тарифа или премиум-поле не по плану, 404 — чужая/несуществующая
страница, 409 — slug занят.
См. также
Заголовок раздела «См. также»- Активные мониторы — основной источник данных для страницы.
- Heartbeat — «тихие» задачи тоже попадают на страницу.
- Доставка и эскалация — как то же падение доходит до вашей команды, а не до публики.
- Квоты и тарифы — лимиты на количество страниц.