REST API
Читайте и записывайте свои мониторы, читайте и обновляйте инциденты, загружайте source map. Всё здесь использует те же проверки прав и тот же фильтр разделения клиентов, что и панель, — отдельной реализации на стороне API, которая могла бы от неё разойтись, не существует.
Входит в любой тариф, включая бесплатный. Ничего докупать не нужно, и нет уровня, который это открывает.
Базовый URL
https://vitrinaengine.com/api/v1Машиночитаемое описание
/openapi.json — документ OpenAPI 3.1, покрывающий все маршруты на этой странице. Направьте на него генератор клиента или передайте его агенту, которому нужно обращаться к этому API без объяснений.
Он генерируется из самого сервиса, а не пишется рядом: маршруты берутся из поверхности, которую API публикует на GET /, структуры и пояснения к ним — из тех же файлов .proto по адресу /api/proto, а ограничения частоты — из кода, который их применяет. Сборка падает, когда документ и API расходятся, поэтому он описывает то, что отдаётся, а не то, что было верно при последней правке.
Он описывает структуры. Причины — на этой странице: что чужой id отвечает 404, чтоconfig никогда не возвращается. Их не напишет ни один генератор.
Для агентов: MCP
https://vitrinaengine.com/mcp — сервер Model Context Protocol. Добавьте его в MCP-клиент с API-ключом в качестве bearer-токена, и агент сможет читать ваши мониторы, инциденты, ошибки и аналитику — а также создавать, изменять и удалять мониторы — без объяснений того, как устроен этот API.
{
"mcpServers": {
"vitrina-engine": {
"type": "http",
"url": "https://vitrinaengine.com/mcp",
"headers": { "Authorization": "Bearer vte_your_key_here" }
}
}
}Он читает всё и пишет три вещи. Каждая операция GET на этой странице — инструмент, и ещё три: POST /monitors, PATCH /monitors/{id} и DELETE /monitors/{id}. Ничего другого, что пишет, через него не достать — ни рабочее пространство, ни участника, ни ключ API, ни страницу статуса, ни ваш аккаунт: инструмента для них попросту нет, а список разрешённых записей — три строки, которые кто-то должен добавить намеренно.
Ключ без права записи по-прежнему получит отказ. Вызов инструмента — это тот же запрос, который ключ сделал бы напрямую, с той же проверкой прав и тем же фильтром арендатора: агент с ключом только для чтения читает. Дайте ему такой, если не хотите, чтобы он менял ваш мониторинг, и рассчитывайте, что клиент переспросит перед удалением: вместе с монитором уходит его история, и вернуть её нельзя. Чтобы монитор замолчал, но остался, поставьте его на паузу через PATCH.
Поскольку это тот же самый запрос, он видит ровно то, что видит ключ, пишет ту же запись в журнал аудита, что и панель, и расходует тот же лимит частоты — дважды: один раз на вызов и один раз на запрос внутри него. Запись расходует лимит записи.
Одно стоит знать до того, как агент возьмётся править монитор: config заменяется целиком, а не сливается. Сначала прочитайте монитор, измените нужное поле и отправьте объект целиком. Частичная конфигурация молча отбросит заголовки и учётные данные, от которых зависит проверка, и оставит её работать.
Аутентификация
Создайте ключ в разделе Настройки → Ключи API и отправляйте его как токен bearer. Ключ показывается один раз, при создании, и хранится только в виде хеша — если вы его потеряете, создайте новый.
curl https://vitrinaengine.com/api/v1/summary \
-H "Authorization: Bearer vte_your_key_here"Ключ никогда не даёт больше, чем было у того, кто его создал. Он привязан к членству этого человека и потому несёт его роль: ключ, созданный тем, у кого роль только для чтения, не сможет писать, что бы вы ни отправили. Ключ можно также ограничить одним рабочим пространством — тогда каждый ответ фильтруется по нему, а всё за его пределами для этого ключа не существует.
Удаление человека из вашей организации отзывает созданные им ключи в той же операции. Отзыв доступа должен забирать учётные данные вместе с ним, иначе у человека остаётся рабочий.
Соглашения
Каждый успешный ответ — это JSON-объект со свойством data. Каждая неудача — это JSON-объект со свойством error, содержащим фразу, предназначенную человеку.
{ "data": { "id": "8f14e45f-…", "name": "Marketing site" } }
{ "error": "This key cannot create monitors." }Ответы отправляются с Cache-Control: private, no-store. Это данные, привязанные к ключу, на общем источнике, и они не должны храниться ни одним прокси.
Protobuf и gzip
Каждая конечная точка под /api/v1 также говорит на Protocol Buffers. С Accept: application/x-protobuf ответ приходит в protobuf; если отправить body в protobuf с Content-Type: application/x-protobuf, ответ вернётся в том же формате. Без того и другого — JSON, как и прежде.
curl https://vitrinaengine.com/api/v1/monitors \
-H "Authorization: Bearer vte_your_key_here" \
-H "Accept: application/x-protobuf" \
--compressed -o monitors.pbСообщения опубликованы: /api/proto перечисляет все файлы. Скачайте их с сохранением путей и укажите этот каталог в protoc -I. Три вещи отличаются от того, чего обычно ждут от protobuf: поле, которое может быть пустым, в JSON равно null, а в protobuf отсутствует; несколько полей, форма которых меняется, — например, config монитора или канала, — это JSON-документы внутри строки; а StringList оборачивает список, который сам может быть null.
Gzip работает в обе стороны: Accept-Encoding: gzip для ответов, Content-Encoding: gzip для запросов. Поскольку два клиента теперь могут получить разные байты по одному URL, каждый ответ несёт Vary: Accept, Content-Type.
Коды состояния
| Код | Значение |
|---|---|
200 | Всё в порядке. 201, когда что-то было создано. |
400 | Тело не было JSON, не хватает поля или ничего не запрошено. |
401 | Ключа нет либо он недействителен. Намеренно никогда не сообщается, что именно из двух: сообщение, которое их различает, — это способ подбирать ключи. |
403 | Действительный ключ без права на это действие. Сообщение называет право. |
404 | Такой записи для вас нет. См. ниже. |
413 | Source map сверх предельного размера. |
429 | Сверх лимита частоты. Несёт Retry-After. См. ниже. |
Коды отказа
В каждом ответе не из диапазона 2xx рядом с фразой есть code — not_found, limit_reached, rate_limited — и ветвиться следует именно по нему. Строка error — это проза для человека, читающего журнал, и её могут переписать в любой момент; код же стабилен. Там, где фраза называет значение, vars несёт эти значения строками, чтобы вы поставили их туда, куда их просит ваша грамматика, а не вытаскивали из английского текста.
{
"error": "The Pro plan includes 50 monitors, and you have 50.",
"code": "limit_reached",
"vars": { "plan": "pro", "limit": "50", "current": "50", "resource": "monitors" }
}Именно отсюда наши собственные приложения рисуют отказ на языке читателя. С незнакомым кодом поступайте так же — возвращайтесь к error — потому что коды добавляются без смены версии.
Лимиты частоты
На организацию, а не на ключ — создание ещё одного ключа их не поднимает.
| Категория | Лимит |
|---|---|
Чтения (GET) | 120 в минуту |
Записи (POST, PATCH, DELETE) | 30 в минуту |
| Загрузки source map | 300 в час |
Сверх лимита возвращается 429 с заголовком Retry-After, где указано целое число секунд до сброса окна. Соблюдайте его, а не повторяйте запрос сразу.
Они заданы там, где обычная интеграция до них не доходит: настенная панель, опрашивающая раз в десять секунд, тратит шесть из 120 чтений. На всех тарифах они одинаковы, потому что коллекционные конечные точки возвращают всё одним ответом — аккаунту с пятьюстами мониторами не нужно больше запросов, чем аккаунту с двадцатью. У source map почасовой лимит, потому что они приходят пачкой во время деплоя, где у фронтенда легко бывает сотня фрагментов.
404 вместо 403, намеренно
На идентификатор, принадлежащий другому клиенту, приходит «Not found.», а не «Forbidden.» Ответ 403 подтвердил бы, что запись существует, и превратил бы конечную точку в способ проверять, настоящий ли идентификатор. Не читайте 404 как доказательство того, что нигде ничего нет, — только того, что нет ничего, что этот ключ вправе видеть.
Конечные точки
GET /search
Всё в аккаунте, что совпадает с ?q=: мониторы, инциденты и их разборы, серверы, страницы статуса, рабочие пространства, каналы уведомлений, окна обслуживания, проекты ошибок и проблемы, сайты аналитики, участники и приглашения, ключи API, агенты и частные зонды. Один запрос вместо запроса на каждый тип.
?type= сужает выборку до списка типов через запятую, а ?limit= ограничивает число результатов каждого типа от 1 до 20 (по умолчанию 5). Каждый тип требует того же разрешения, что и его собственная точка доступа; тип, который ваш ключ не может читать, отсутствует так же, как тип без совпадений. Ничего запечатанного, хешированного или секретного не ищется и не возвращается.
Параметры и ответы
| Имя | Где | Тип |
|---|---|---|
limit | query | integer |
q | query | string |
type | query | string |
| Ответ | SearchResponse |
|---|---|
| Коды состояния | 200 400 401 403 404 429 |
| Лимит частоты | 120 в минуту |
GET /summary
Счётчики, для настенной панели или ежедневной сводки. Требует monitor:read.
{
"data": {
"total": 42, "up": 39, "degraded": 1, "down": 1,
"paused": 1, "pending": 0,
"openIncidents": 2, "suppressedIncidents": 1
}
}suppressedIncidents считает инциденты, придержанные потому, что они входят в зону влияния другого. Они настоящие, и они не отдельные сбои.
Параметры и ответы
Объявленных параметров и полей тела нет.
| Ответ | GetSummaryResponse |
|---|---|
| Коды состояния | 200 400 401 403 404 429 |
| Лимит частоты | 120 в минуту |
GET /setup
Что новому аккаунту ещё предстоит сделать — те же четыре вопроса, что задаёт список первоначальной настройки в панели, вычисляемые при каждом чтении. Требует monitor:read.
{
"data": {
"hasMonitor": true,
"hasConfirmedChannel": false,
"hasStatusPage": false,
"hasColleague": false,
"dismissedAt": null
}
}hasConfirmedChannel спрашивает, может ли оповещение действительно дойти, а не существует ли канал. Однажды эти две вещи разошлись скверно, и список, отмечающий оповещения настроенными для канала, на который мы отказываемся отправлять, повторял бы ту же ошибку в качестве успокоения.
hasColleague считает участие в организации, но никогда — приглашения: отправленное и не принятое приглашение никого не добавило.
Не встроено в /summary, потому что тот эндпоинт — это счётчики по статусам и ничего больше: настенная панель, опрашивающая его каждые несколько секунд, не хочет пяти лишних запросов ради подсказки, обращённой к тому, кто настроил аккаунт месяцы назад.
Параметры и ответы
Объявленных параметров и полей тела нет.
| Ответ | GetSetupResponse |
|---|---|
| Коды состояния | 200 400 401 403 404 429 |
| Лимит частоты | 120 в минуту |
DELETE /setup
Пропускает список. Требует org:update, потому что это одна строка на всю организацию, а не личная настройка: коллеге, пришедшему позже, его снова не покажут.
Больше в аккаунте ничего не меняется, и запись аудита не пишется: скрытие подсказки ничего не меняет в том, как продукт работает, оповещает или выставляет счета.
Параметры и ответы
Объявленных параметров и полей тела нет.
| Ответ | DismissedResponse |
|---|---|
| Коды состояния | 200 400 401 403 404 429 |
| Лимит частоты | 30 в минуту |
GET /monitors
Все мониторы, которые видит ключ. Требует monitor:read. Каждый несёт id, name, kind, status, statusSince, enabled, intervalSeconds, tags, workspace, lastCheckedAt, lastResponseTimeMs, lastMessage, uptime24h, uptime30d и openIncidentId.
?tag=prod,eu сужает список до мониторов, несущих все названные метки — И, а не ИЛИ, поэтому вторая метка всегда возвращает меньше мониторов, а не больше. Фильтр в панели читается так же. Метки хранятся в нижнем регистре, и значение здесь приводится так же перед сравнением, так что ?tag=Prod их находит. Запись, которая не может быть меткой — пустая или длиннее, чем метке позволено, — отбрасывается, а остальной фильтр всё равно применяется, вместо отказа в запросе: ссылка не должна переставать работать из-за того, что одну из её меток переименовали.
config не возвращается никогда. Для некоторых видов он содержит заголовки запроса и учётные данные, и ключ с правом только на чтение не должен быть способом прочитать обратно секреты, которые кто-то ввёл в форму.
Параметры и ответы
| Имя | Где | Тип |
|---|---|---|
tag | query | string |
| Ответ | ListMonitorsResponse |
|---|---|
| Коды состояния | 200 400 401 403 404 429 |
| Лимит частоты | 120 в минуту |
POST /monitors
Требует monitor:create. Обязательны name, kind, workspaceId и config. Необязательны: intervalSeconds (по умолчанию 300), confirmations, dependsOn.
curl -X POST https://vitrinaengine.com/api/v1/monitors \
-H "Authorization: Bearer vte_…" \
-H "Content-Type: application/json" \
-d '{
"name": "Marketing site",
"kind": "http",
"workspaceId": "…",
"intervalSeconds": 60,
"config": {
"url": "https://example.com",
"assertions": [{ "type": "status_code", "operator": "lt", "value": "400" }]
}
}'Отвечает 201 с новым идентификатором. Интервал поднимается до нижней границы вашего тарифа, а не отклоняется: запрос на 10 секунд на тарифе с нижней границей 60 даст 60 — прочитайте обратно, если это важно.
Параметры и ответы
| Имя | Где | Тип |
|---|---|---|
name | body | string |
kind | body | string |
workspaceId | body | string |
config | body | string |
intervalSeconds | body | integer |
confirmations | body | integer |
dependsOn | body | string[] |
probeId | body | string |
tags | body | string[] |
| Ответ | CreatedResponse |
|---|---|
| Коды состояния | 201 400 401 403 404 429 |
| Лимит частоты | 30 в минуту |
GET /monitors/{id}
Один монитор, со всем, что даёт список, плюс paused. Требует monitor:read.
Для mail_posture, tls_audit и snmp добавляется lastCheckDetail — результат последней проверки: находки за статусом, каждая со стабильным code и severity, а также оценка TLS или показания SNMP. До первой проверки равен null, у остальных типов отсутствует.
Параметры и ответы
| Имя | Где | Тип |
|---|---|---|
id обязательный | path | string |
| Ответ | GetMonitorResponse |
|---|---|
| Коды состояния | 200 400 401 403 404 429 |
| Лимит частоты | 120 в минуту |
PATCH /monitors/{id}
Отправляйте только то, что хотите изменить: name, intervalSeconds, confirmations, enabled, config или paused.
paused проверяется против monitor:pause, а всё остальное против monitor:update, по отдельности — ключ, которому разрешено ставить на паузу, но не редактировать, всё равно может поставить на паузу. Пустое изменение даёт 400.
Монитор перечитывается и возвращается заново, а не отражается ваш ввод, — так что вы видите то, что было сохранено.
# Silence a monitor for the length of a deploy.
curl -X PATCH https://vitrinaengine.com/api/v1/monitors/$ID \
-H "Authorization: Bearer vte_…" \
-H "Content-Type: application/json" \
-d '{"paused": true}'Параметры и ответы
| Имя | Где | Тип |
|---|---|---|
id обязательный | path | string |
name | body | string |
intervalSeconds | body | integer |
confirmations | body | integer |
enabled | body | boolean |
paused | body | boolean |
config | body | string |
tags | body | StringList |
| Ответ | UpdatedMonitorResponse |
|---|---|
| Коды состояния | 200 400 401 403 404 429 |
| Лимит частоты | 30 в минуту |
DELETE /monitors/{id}
Требует monitor:delete. Отвечает { "data": { "id": "…", "deleted": true } }.
Параметры и ответы
| Имя | Где | Тип |
|---|---|---|
id обязательный | path | string |
| Ответ | AckResponse |
|---|---|
| Коды состояния | 200 400 401 403 404 429 |
| Лимит частоты | 30 в минуту |
GET /hosts · POST /hosts
Серверы — второй способ группировать мониторы: рабочее пространство говорит, чей это монитор, а сервер — на чём он работает. Полностью необязательно: аккаунт, который не создаст ни одного, ничего не теряет, и большинство мониторов не называют сервера вовсе.
У каждого сервера есть status, pinned и spanning. status берётся только из мониторов, работающих на этом сервере и больше нигде. Монитор, охватывающий несколько машин, попадает в spanning и не окрашивает ни одну из них: сбой на балансируемой точке говорит, что сломан сервис, а не какая машина его сломала. Сервер без собственных мониторов отвечает "status": null, а не up.
POST требует monitor:create и name. Необязательные provider, address и notes — ваши заметки, продукт их не использует. Имя, совпадающее с уже существующим (регистр и завершающая точка игнорируются), отвечает 409.
Параметры и ответы
GET /hosts
Объявленных параметров и полей тела нет.
| Ответ | ListHostsResponse |
|---|---|
| Коды состояния | 200 400 401 403 404 429 |
| Лимит частоты | 120 в минуту |
POST /hosts
| Имя | Где | Тип |
|---|---|---|
name | body | string |
provider | body | string |
address | body | string |
notes | body | string |
| Ответ | CreatedResponse |
|---|---|
| Коды состояния | 201 400 401 403 404 429 |
| Лимит частоты | 30 в минуту |
GET /hosts/{id} · PATCH /hosts/{id} · DELETE /hosts/{id}
Подробности содержат сервер и его мониторы, каждый с флагом pinned, а если он не установлен — с alsoOn: другие машины, с которых монитор отвечает. Именно это читают перед перезагрузкой. DELETE выводит сервер из списка, и все его мониторы продолжают работать: у сервера нет ни своего расписания, ни своей истории.
Параметры и ответы
GET /hosts/{id}
| Имя | Где | Тип |
|---|---|---|
id обязательный | path | string |
| Ответ | GetHostResponse |
|---|---|
| Коды состояния | 200 400 401 403 404 429 |
| Лимит частоты | 120 в минуту |
PATCH /hosts/{id}
| Имя | Где | Тип |
|---|---|---|
id обязательный | path | string |
name | body | string |
provider | body | string |
address | body | string |
notes | body | string |
| Ответ | AckResponse |
|---|---|
| Коды состояния | 200 400 401 403 404 429 |
| Лимит частоты | 30 в минуту |
DELETE /hosts/{id}
| Имя | Где | Тип |
|---|---|---|
id обязательный | path | string |
| Ответ | AckResponse |
|---|---|
| Коды состояния | 200 400 401 403 404 429 |
| Лимит частоты | 30 в минуту |
POST /hosts/{id}/monitors · DELETE /hosts/{id}/monitors
Один монитор на сервер или с сервера — через monitorId в теле или ?monitorId=. Намеренно не целый список: в агентском аккаунте на сервере живут мониторы нескольких рабочих пространств, и тот, кто заменил бы «список», заменил бы строки, которых ему не показывали. Обе операции идемпотентны.
Параметры и ответы
POST /hosts/{id}/monitors
| Имя | Где | Тип |
|---|---|---|
id обязательный | path | string |
monitorId | body | string |
| Ответ | AckResponse |
|---|---|
| Коды состояния | 200 400 401 403 404 429 |
| Лимит частоты | 30 в минуту |
DELETE /hosts/{id}/monitors
| Имя | Где | Тип |
|---|---|---|
id обязательный | path | string |
monitorId | query | string |
monitorId | body | string |
| Ответ | AckResponse |
|---|---|
| Коды состояния | 200 400 401 403 404 429 |
| Лимит частоты | 30 в минуту |
GET /monitors/{id}/hosts · PUT /monitors/{id}/hosts
Обратная сторона той же связи, и здесь это именно целый список: связи принадлежат одному монитору, поэтому на другом конце нет ничего, что видело бы другое рабочее пространство. Отправьте hostIds; пустой массив — настоящий запрос и означает, что монитор не привязан ни к какому серверу. Чужие идентификаторы отбрасываются, а не отклоняются. В каждом ответе о мониторе есть также hosts — пустой, если серверов нет.
Параметры и ответы
GET /monitors/{id}/hosts
| Имя | Где | Тип |
|---|---|---|
id обязательный | path | string |
| Ответ | ListMonitorHostsResponse |
|---|---|
| Коды состояния | 200 400 401 403 404 429 |
| Лимит частоты | 120 в минуту |
PUT /monitors/{id}/hosts
| Имя | Где | Тип |
|---|---|---|
id обязательный | path | string |
hostIds | body | string[] |
| Ответ | ListMonitorHostsResponse |
|---|---|
| Коды состояния | 200 400 401 403 404 429 |
| Лимит частоты | 30 в минуту |
GET /incidents
Требует incident:read. Принимает ?status= — одно из open, acknowledged, resolved или suppressed — и ?open=true для тех трёх, что не разрешены. Нераспознанный статус игнорируется, а не отклоняется, — ровно так же, как ?limit= (по умолчанию 50) ограничивается до 200, а не отвергается.
Каждый несёт id, monitorId, monitorName, title, cause, status, severity, startedAt, resolvedAt, durationSeconds, acknowledgedAt и rootIncidentId.
rootIncidentId — то поле, на которое стоит смотреть, когда вы строите ленту оповещений. Если оно заполнено, этот инцидент входит в зону влияния другого: упал узел с базой данных, а это один из двенадцати сервисов за ним. Пропустите такие — и получите одно оповещение вместо тринадцати.
Параметры и ответы
| Имя | Где | Тип |
|---|---|---|
limit | query | integer |
open | query | boolean |
status | query | string |
| Ответ | ListIncidentsResponse |
|---|---|
| Коды состояния | 200 400 401 403 404 429 |
| Лимит частоты | 120 в минуту |
GET /incidents/{id}
Один инцидент. Требует incident:read. Те же поля, что и в списке, плюс workspaceId.
Он не возвращает хронологию инцидента — комментарии и смены статуса, видные в панели, на этой конечной точке отсутствуют. Если они вам нужны, скажите, и их можно добавить; описывать их здесь, пока их нет, было бы хуже, чем этот пробел.
Параметры и ответы
| Имя | Где | Тип |
|---|---|---|
id обязательный | path | string |
| Ответ | GetIncidentResponse |
|---|---|
| Коды состояния | 200 400 401 403 404 429 |
| Лимит частоты | 120 в минуту |
PATCH /incidents/{id}
Отправьте status со значением "acknowledged" или "resolved", либо comment, либо и то и другое. Каждое проверяется против своего права: incident:acknowledge, incident:resolve, incident:comment. Добавление "publish": true к комментарию помещает его на страницу статуса и дополнительно требует status_page:manage.
# A runbook that fixed the thing itself can close its own incident.
curl -X PATCH https://vitrinaengine.com/api/v1/incidents/$ID \
-H "Authorization: Bearer vte_…" \
-H "Content-Type: application/json" \
-d '{"status": "resolved", "comment": "Restarted by runbook.", "publish": true}'Параметры и ответы
| Имя | Где | Тип |
|---|---|---|
id обязательный | path | string |
status | body | string |
comment | body | string |
publish | body | boolean |
| Ответ | GetIncidentResponse |
|---|---|
| Коды состояния | 200 400 401 403 404 429 |
| Лимит частоты | 30 в минуту |
PUT /incidents/{id}/postmortem
Создаёт или заменяет разбор инцидента. Требуется incident:resolve, и инцидент должен быть уже закрыт — иначе 409 с кодом incident_not_resolved. Отправьте body в Markdown, от 1 до 50 000 символов после обрезки пробелов (postmortem_required, postmortem_too_long). У инцидента один разбор, поэтому каждый вызов заменяет предыдущий. Ответ — инцидент в том виде, в каком его возвращает GET /incidents/{id}.
GET /incidents/{id} содержит его как postmortem: { body, authorName, updatedAt } или null, а список — hasPostmortem. Отображайте body как Markdown и никогда как HTML: это текст, набранный человеком. Разбор внутренний и никогда не появляется на странице статуса.
curl -X PUT https://vitrinaengine.com/api/v1/incidents/$ID/postmortem \
-H "Authorization: Bearer vte_…" \
-H "Content-Type: application/json" \
-d '{"body": "## What happened\n\nThe connection pool ran dry after a deploy."}'Параметры и ответы
| Имя | Где | Тип |
|---|---|---|
id обязательный | path | string |
body | body | string |
| Ответ | GetIncidentResponse |
|---|---|
| Коды состояния | 200 400 401 403 404 429 |
| Лимит частоты | 30 в минуту |
GET /probes
Собственные частные зонды вашей организации: id, name, lastSeenAt, version, hostname, monitorCount, revoked и createdAt. Требуется agent:read. Именно их предлагает выбор зонда у монитора.
Поля региона нет и не будет. Вы выбираете зонд по имени; где за ним выполняется проверка, решаем и меняем мы. Отозванные зонды перечислены с revoked: true и больше не получают работу, поэтому не предлагайте их.
Параметры и ответы
Объявленных параметров и полей тела нет.
| Ответ | ListProbesResponse |
|---|---|
| Коды состояния | 200 400 401 403 404 429 |
| Лимит частоты | 120 в минуту |
GET /monitors/{id}/push-url
URL для пингов heartbeat-монитора, показанный снова. Требуется monitor:read. url равен null для типа, который не пингуют, и для токена, который больше нельзя расшифровать, — монитор продолжает работать, а перевыпуск токена в панели даёт URL, который можно показать.
Параметры и ответы
| Имя | Где | Тип |
|---|---|---|
id обязательный | path | string |
| Ответ | GetPushUrlResponse |
|---|---|
| Коды состояния | 200 400 401 403 404 429 |
| Лимит частоты | 120 в минуту |
GET /organizations · PUT /organizations/active
Организации, в которых состоит вошедший пользователь, у каждой — его role там и active у текущей, а также переключение между ними. Отправьте organizationId; в ответе — членство, которое теперь используется.
Только для сессии входа. Ключ принадлежит одному членству и получает 403 с session_required. Переключение переносит все сессии этого человека, включая панель, а организация, в которой он не состоит, отвечает 404.
Параметры и ответы
GET /organizations
Объявленных параметров и полей тела нет.
| Ответ | ListOrganizationsResponse |
|---|---|
| Коды состояния | 200 400 401 403 404 429 |
| Лимит частоты | 120 в минуту |
PUT /organizations/active
| Имя | Где | Тип |
|---|---|---|
organizationId | body | string |
| Ответ | ActiveOrganizationResponse |
|---|---|
| Коды состояния | 200 400 401 403 404 429 |
| Лимит частоты | 30 в минуту |
POST /sms/enrolment · POST /sms/enrolment/confirm
Подключите свой мобильный номер для SMS-оповещений: отправьте phone (и при желании name), и мы пришлём на него шестизначный код; отправьте этот code на второй маршрут для подтверждения. Требуются notification_channel:manage и сессия входа — номер добавляет человек, у которого телефон, а не ключ.
Номер сохраняется неподтверждённым и ничего не получает, пока код не вернётся. Повторное подключение заменяет прежний номер. Отказы: sms_not_included (402, в тарифе нет SMS), not_a_phone_number, sms_country_unsupported, sms_route_not_open, verification_code_not_sent (502; вызовите снова, чтобы получить новый код), verification_code_malformed и verification_code_incorrect. Успешный запуск возвращает country и caveat — sender-replaced, registration-pending, unverified или null, — это стоит показать, прежде чем на номер начнут полагаться.
Параметры и ответы
POST /sms/enrolment
| Имя | Где | Тип |
|---|---|---|
phone | body | string |
name | body | string |
| Ответ | SmsEnrolmentResponse |
|---|---|
| Коды состояния | 201 400 401 403 404 429 |
| Лимит частоты | 30 в минуту |
POST /sms/enrolment/confirm
| Имя | Где | Тип |
|---|---|---|
code | body | string |
| Ответ | SmsConfirmedResponse |
|---|---|
| Коды состояния | 200 400 401 403 404 429 |
| Лимит частоты | 30 в минуту |
POST /channels/{id}/confirmation
Повторно отправляет письмо подтверждения для email-канала. Требуется notification_channel:manage. Каждый вызов выпускает новую ссылку, а прежняя перестаёт работать. sent: false означает, что отправлять было нечего — адрес уже подтверждён, — и подтверждённому адресу этим путём ничего повторно не пишут. Если почтовый провайдер отказал в отправке, ответ — 502 с confirmation_not_sent.
Параметры и ответы
| Имя | Где | Тип |
|---|---|---|
id обязательный | path | string |
| Ответ | ChannelConfirmationResponse |
|---|---|
| Коды состояния | 200 400 401 403 404 429 |
| Лимит частоты | 30 в минуту |
PATCH /workspaces/{id}
Переименовывает рабочее пространство. Требуется workspace:update. name обязателен (от 1 до 80 символов), clientReference необязателен — null его очищает. Slug не меняется никогда, потому что входит в URL страниц статуса. В ответе — рабочее пространство в новом виде.
Параметры и ответы
| Имя | Где | Тип |
|---|---|---|
id обязательный | path | string |
name | body | string |
clientReference | body | requests.NullableString |
| Ответ | UpdatedWorkspaceResponse |
|---|---|
| Коды состояния | 200 400 401 403 404 429 |
| Лимит частоты | 30 в минуту |
POST /sourcemaps
Загрузите source map, чтобы минифицированные стеки вызовов разбирались. Требует monitor:create. Обязательны projectRef (число) и filename, а также сама карта — либо как map (JSON), либо как mapGzipBase64. Необязательны debugId и release.
Отправьте debug id или release. Карту без того и другого невозможно сопоставить со стеком вызовов, и она будет лежать без дела. Сопоставление идёт сначала по debugId, затем по release и имени файла.
Карты разбираются при чтении проблемы, а не при загрузке, поэтому карта, загруженная уже после того, как ошибки пришли, всё равно помогает — и это обычный порядок.
Параметры и ответы
| Имя | Где | Тип |
|---|---|---|
projectRef | body | integer |
filename | body | string |
debugId | body | string |
release | body | string |
map | body | string |
mapGzipBase64 | body | string |
| Ответ | UploadedSourceMapResponse |
|---|---|
| Коды состояния | 201 400 401 403 404 429 |
| Лимит частоты | 300 в час |
POST /symbols
То же самое для нативного приложения: загрузите .dSYM с iOS или mapping.txt от R8 на Android, чтобы стеки падений разбирались. Требует monitor:create. Двадцать загрузок в час и двадцать сборок, которые хранятся на проект.
В отличие от /sourcemaps, телом запроса служит сам сжатый gzip файл, а метаданные идут в заголовках: X-Vitrina-Project-Ref, X-Vitrina-Platform (ios или android), X-Vitrina-Symbol-Name и X-Vitrina-Release. dSYM весит десятки мегабайт, а base64 внутри поля JSON стоит ещё трети объёма передачи.
Две платформы сопоставляются по-разному. Кадр стека на iOS называет Mach-O UUID образа, которому принадлежит, поэтому загрузка для iOS обязана нести X-Vitrina-Debug-Ids и сопоставляется точно по нему. R8 такого идентификатора не выдаёт, поэтому mapping для Android сопоставляется только по release — и он должен в точности совпадать с тем, что сообщает приложение: <applicationId>@<versionName>+<versionCode>. Ошибка в одной цифре — и кадры разберутся в строки, которые выглядят правдоподобно и неверны.
Повторная загрузка той же сборки заменяет её символы, а не завершается ошибкой, так что перезапущенная задача релиза — не ошибка. Как и с source map, разбор происходит при чтении проблемы.
Параметры и ответы
Объявленных параметров и полей тела нет.
| Ответ | UploadedSymbolsResponse |
|---|---|
| Коды состояния | 200 400 401 403 404 429 |
| Лимит частоты | 20 в час |
GET /errors/projects
Проекты отслеживания ошибок, которые есть у этого аккаунта. Требует monitor:read. Каждый несёт ref — число из DSN — а также publicKey, unresolved и eventsLast24h. Только чтение: проект создаётся с ключом, который всё равно нужно вставить в конфигурацию приложения, поэтому здесь нет ничего, что конечная точка могла бы довести до конца.
Параметры и ответы
Объявленных параметров и полей тела нет.
| Ответ | ListErrorProjectsResponse |
|---|---|
| Коды состояния | 200 400 401 403 404 429 |
| Лимит частоты | 120 в минуту |
GET /errors/issues
Сгруппированные ошибки, самая свежая активность первой. Требует monitor:read. Проблема — это отпечаток, а не событие, поэтому тысяча появлений одного исключения — одна строка с timesSeen, равным тысяче. ?status= по умолчанию unresolved и принимает также resolved, ignored или all; ?project= фильтрует по идентификатору проекта, ?q= ищет по типу, значению и виновнику, а ?limit= ограничен 200. spark — двадцать четыре почасовых значения, от старых к новым.
Параметры и ответы
| Имя | Где | Тип |
|---|---|---|
limit | query | integer |
project | query | string |
q | query | string |
status | query | string |
| Ответ | ListIssuesResponse |
|---|---|
| Коды состояния | 200 400 401 403 404 429 |
| Лимит частоты | 120 в минуту |
GET /errors/issues/{id}
Одна проблема со своим стеком. Требует monitor:read. exceptions — это собственная цепочка SDK: брошенная ошибка последней, её причины перед ней, — и её кадры разбираются по любой загруженной вами source map: тот же разбор, что делает панель, чтобы скрипт и человек, с которым вы говорите, никогда не смотрели на разные стеки. Карта, которую применить не удалось, откатывается к исходным кадрам, а не роняет запрос.
lastEvent несёт две отметки времени намеренно. occurredAt — то, что сообщил SDK, а receivedAt — когда приём записал событие; устройство, бывшее офлайн, или очередь, которая накопилась, — это ровно промежуток между ними.
Параметры и ответы
| Имя | Где | Тип |
|---|---|---|
id обязательный | path | string |
| Ответ | GetIssueResponse |
|---|---|
| Коды состояния | 200 400 401 403 404 429 |
| Лимит частоты | 120 в минуту |
PATCH /errors/issues/{id}
Решить, игнорировать или открыть заново: { "status": "resolved" }. Требует monitor:update. ignoreHours может сопровождать ignored, чтобы позже снять игнорирование, и ограничен 90 днями.
Решение записывает release, в котором проблема была решена, поэтому запоздавшее событие из более старого развёртывания её не переоткрывает. Когда SDK не прислал release, сравнивать не с чем, и любое последующее событие её всё же переоткроет: раз отличить опоздавшего от регрессии нельзя, безопасно считать, что ошибка вернулась.
Параметры и ответы
| Имя | Где | Тип |
|---|---|---|
id обязательный | path | string |
status | body | string |
ignoreHours | body | integer |
| Ответ | IssueStatusResponse |
|---|---|
| Коды состояния | 200 400 401 403 404 429 |
| Лимит частоты | 30 в минуту |
GET /analytics
Сайты, которые измеряет этот аккаунт. Требует monitor:read. Несёт publicId — значение, которое идёт в тег скрипта и публично по замыслу, — а также viewsLast24h и trafficAlerting, истинный, пока трафик сайта ниже обычного для этого часа.
Параметры и ответы
Объявленных параметров и полей тела нет.
| Ответ | ListAnalyticsSitesResponse |
|---|---|
| Коды состояния | 200 400 401 403 404 429 |
| Лимит частоты | 120 в минуту |
GET /analytics/{id}
Числа одного сайта, для таблицы или отчёта. Требует monitor:read. ?days= по умолчанию 7 и ограничен 365. ?dimensions= принимает список через запятую из path, referrer, country, browser, os, device, utm_source, utm_medium, utm_campaign и event; неизвестное измерение даёт 400, а не пустой ответ, потому что опечатка, возвращающая ничего, читается как “трафика нет”.
Показатель посетителей называется dailyUniqueVisitors, и это именно то, что он есть. Это сумма дневных уникальных, и ничем другим она быть не может: секрет за хешем посетителя уничтожается в конце своих суток UTC, поэтому тот, кто заходил в два разных дня, считается дважды, и ключа, который бы их связал, не существует. Так работает замысел приватности, это не приближение, — но поле с именем visitors рядом с 30-дневным периодом подталкивало бы вас сообщить число, которое значит совсем другое.
curl "https://vitrinaengine.com/api/v1/analytics/$ID?days=30&dimensions=path,referrer" \
-H "Authorization: Bearer vte_…"Параметры и ответы
| Имя | Где | Тип |
|---|---|---|
id обязательный | path | string |
days | query | integer |
| Ответ | GetAnalyticsSiteResponse |
|---|---|
| Коды состояния | 200 400 401 403 404 429 |
| Лимит частоты | 120 в минуту |
GET /billing
Тариф и то, где он оплачивается. Требует billing:read. Несёт plan, status, cadence, currentPeriodEnd, cancelAt, addOnPacks и последнюю дюжину чеков.
source — это paddle, apple, manual или none. Это важно: подписку, купленную в приложении для iOS, меняет Apple, и тариф, карта и отмена живут в настройках App Store у клиента, а не здесь. canCheckoutOnWeb и canPurchaseInApp говорят, какие предложения безопасно показывать, чтобы клиенту не приходилось выводить это правило заново и ошибиться в ту сторону, где с человека спишут дважды.
Параметры и ответы
Объявленных параметров и полей тела нет.
| Ответ | GetBillingResponse |
|---|---|
| Коды состояния | 200 400 401 403 404 429 |
| Лимит частоты | 120 в минуту |
GET /monitors/{id}/history
Ежедневная доступность одного монитора. Требует monitor:read. Принимает days, вплоть до срока хранения истории на вашем тарифе.
Считается по дневным сводкам, а не по отдельным результатам проверок, — поэтому она продолжает отвечать за периоды, сырые результаты которых хранение уже удалило.
Параметры и ответы
| Имя | Где | Тип |
|---|---|---|
id обязательный | path | string |
days | query | integer |
| Ответ | GetMonitorHistoryResponse |
|---|---|
| Коды состояния | 200 400 401 403 404 429 |
| Лимит частоты | 120 в минуту |
GET /monitors/{id}/graph · GET /monitors/graphs
Последние часы одного монитора, час за часом, или всех мониторов, которые видит этот ключ, в одном ответе — то, что панель рисует в строке и на странице монитора. Требует monitor:read. Принимает hours: по умолчанию 24, не больше 168.
График uptime содержит долю успешных проверок за каждый час и null для часа без проверок; график value для количественных видов содержит показания в виде рядов с единицей измерения. В окне присутствует каждый час, записано в нём что-то или нет, чтобы пропуск был виден как пропуск. Показания приходят такими, какими были измерены: у ряда raw нет известной шкалы — рисуйте его относительно его собственного максимума и никогда не выдавайте эту долю за показание.
Параметры и ответы
GET /monitors/{id}/graph
| Имя | Где | Тип |
|---|---|---|
id обязательный | path | string |
hours | query | integer |
| Ответ | GetMonitorGraphResponse |
|---|---|
| Коды состояния | 200 400 401 403 404 429 |
| Лимит частоты | 120 в минуту |
GET /monitors/graphs
| Имя | Где | Тип |
|---|---|---|
hours | query | integer |
| Ответ | ListMonitorGraphsResponse |
|---|---|
| Коды состояния | 200 400 401 403 404 429 |
| Лимит частоты | 120 в минуту |
GET /monitors/{id}/channels · PUT /monitors/{id}/channels
Какие каналы оповещает этот монитор и как их задать. Чтение требует notification_channel:read, запись — monitor:update.
PUT заменяет весь набор целиком — отправляйте все нужные идентификаторы каналов, а не только добавляемые. Пустой массив означает, что монитор не оповещает никого: это допустимое желание и скверная случайность.
Параметры и ответы
GET /monitors/{id}/channels
| Имя | Где | Тип |
|---|---|---|
id обязательный | path | string |
| Ответ | GetMonitorChannelsResponse |
|---|---|
| Коды состояния | 200 400 401 403 404 429 |
| Лимит частоты | 120 в минуту |
PUT /monitors/{id}/channels
| Имя | Где | Тип |
|---|---|---|
id обязательный | path | string |
channelIds | body | StringList |
| Ответ | GetMonitorChannelsResponse |
|---|---|
| Коды состояния | 200 400 401 403 404 429 |
| Лимит частоты | 30 в минуту |
GET /incidents/{id}/events
Хронология одного инцидента: смены состояния, подтверждения, комментарии, шаги эскалации. Требует incident:read. Это та самая хронология, которой GET /incidents/{id} не отдаёт.
Параметры и ответы
| Имя | Где | Тип |
|---|---|---|
id обязательный | path | string |
| Ответ | ListIncidentEventsResponse |
|---|---|
| Коды состояния | 200 400 401 403 404 429 |
| Лимит частоты | 120 в минуту |
GET /channels · POST /channels
Чтение требует notification_channel:read, создание — notification_channel:manage. Каждый несёт id, kind, name, target и workspaceId.
secret возвращается только при создании канала и больше никогда. Это ключ подписи для вебхука; хранить его там, откуда его можно прочитать обратно, значит сделать ключ только для чтения способом подделывать подписанные запросы.
Создавать можно те виды, которые человек создаёт в панели: email, webhook, telegram, slack, teams, discord, pagerduty и jsm. SMS, WhatsApp, спутниковую связь и push подключает тот, кто их получает, и через API их создать нельзя — именно это делает их согласием, а не полем, которое кто-то заполнил.
Канал email создаётся неподтверждённым и получает ровно одно письмо с вопросом, хочет ли он оповещений. До согласия больше ничего не отправляется, так что создание канала через API само по себе не ставит адрес в ваш график вызовов.
Параметры и ответы
GET /channels
Объявленных параметров и полей тела нет.
| Ответ | ListChannelsResponse |
|---|---|
| Коды состояния | 200 400 401 403 404 429 |
| Лимит частоты | 120 в минуту |
POST /channels
| Имя | Где | Тип |
|---|---|---|
kind | body | string |
name | body | string |
target | body | string |
workspaceId | body | requests.NullableString |
| Ответ | CreatedChannelResponse |
|---|---|
| Коды состояния | 201 400 401 403 404 429 |
| Лимит частоты | 30 в минуту |
PATCH /channels/{id} · DELETE /channels/{id} · POST /channels/{id}/test
Всем требуется notification_channel:manage. Тестовая отправка отклоняется для адреса, который не подтвердился, — иначе это был бы неограниченный способ писать на адрес без согласия, под услужливой вывеской.
Параметры и ответы
PATCH /channels/{id}
| Имя | Где | Тип |
|---|---|---|
id обязательный | path | string |
enabled | body | boolean |
| Ответ | AckResponse |
|---|---|
| Коды состояния | 200 400 401 403 404 429 |
| Лимит частоты | 30 в минуту |
DELETE /channels/{id}
| Имя | Где | Тип |
|---|---|---|
id обязательный | path | string |
| Ответ | AckResponse |
|---|---|
| Коды состояния | 200 400 401 403 404 429 |
| Лимит частоты | 30 в минуту |
POST /channels/{id}/test
| Имя | Где | Тип |
|---|---|---|
id обязательный | path | string |
| Ответ | TestDeliveryResponse |
|---|---|
| Коды состояния | 200 400 401 403 404 429 |
| Лимит частоты | 30 в минуту |
GET /status-pages · POST /status-pages
Чтение требует status_page:read, запись — status_page:manage. Каждая несёт id, name, slug, visibility, customDomain, domainVerifiedAt, subscribersEnabled, componentCount, workspace и createdAt.
Параметры и ответы
GET /status-pages
Объявленных параметров и полей тела нет.
| Ответ | ListStatusPagesResponse |
|---|---|
| Коды состояния | 200 400 401 403 404 429 |
| Лимит частоты | 120 в минуту |
POST /status-pages
| Имя | Где | Тип |
|---|---|---|
name | body | string |
workspaceId | body | string |
| Ответ | CreatedResponse |
|---|---|
| Коды состояния | 201 400 401 403 404 429 |
| Лимит частоты | 30 в минуту |
GET /status-pages/{id} · PATCH /status-pages/{id} · DELETE /status-pages/{id}
Чтение требует status_page:read; изменение или удаление — status_page:manage.
Параметры и ответы
GET /status-pages/{id}
| Имя | Где | Тип |
|---|---|---|
id обязательный | path | string |
| Ответ | GetStatusPageResponse |
|---|---|
| Коды состояния | 200 400 401 403 404 429 |
| Лимит частоты | 120 в минуту |
PATCH /status-pages/{id}
| Имя | Где | Тип |
|---|---|---|
id обязательный | path | string |
name | body | string |
headline | body | requests.NullableString |
description | body | requests.NullableString |
logoUrl | body | requests.NullableString |
themeAccent | body | string |
language | body | string |
historyDays | body | integer |
showResponseTimes | body | boolean |
showIncidentHistory | body | boolean |
subscribersEnabled | body | boolean |
visibility | body | string |
password | body | requests.NullableString |
customCss | body | requests.NullableString |
hideVitrinaBranding | body | boolean |
groupByHost | body | boolean |
| Ответ | UpdatedStatusPageResponse |
|---|---|
| Коды состояния | 200 400 401 403 404 429 |
| Лимит частоты | 30 в минуту |
DELETE /status-pages/{id}
| Имя | Где | Тип |
|---|---|---|
id обязательный | path | string |
| Ответ | AckResponse |
|---|---|
| Коды состояния | 200 400 401 403 404 429 |
| Лимит частоты | 30 в минуту |
POST /status-pages/{id}/components · DELETE /status-pages/{id}/components
Добавляет монитор на страницу или убирает его оттуда. Требует status_page:manage.
Монитор должен быть в том же рабочем пространстве, что и страница, а не просто в той же организации. В агентском аккаунте именно это не даёт опубликовать монитор одного клиента на странице другого.
Параметры и ответы
POST /status-pages/{id}/components
| Имя | Где | Тип |
|---|---|---|
id обязательный | path | string |
monitorId | body | string |
displayName | body | string |
| Ответ | ComponentAckResponse |
|---|---|
| Коды состояния | 201 400 401 403 404 429 |
| Лимит частоты | 30 в минуту |
DELETE /status-pages/{id}/components
| Имя | Где | Тип |
|---|---|---|
id обязательный | path | string |
component | query | string |
componentId | body | string |
| Ответ | ComponentAckResponse |
|---|---|
| Коды состояния | 200 400 401 403 404 429 |
| Лимит частоты | 30 в минуту |
GET /workspaces · POST /workspaces · DELETE /workspaces/{id}
Чтение требует workspace:read, остальное — своих собственных прав. Каждое несёт id, name, slug, clientReference и isDefault.
Ключ ограниченного участника видит только те рабочие пространства, которыми этот участник ограничен. Рабочее пространство по умолчанию удалить нельзя.
Параметры и ответы
GET /workspaces
Объявленных параметров и полей тела нет.
| Ответ | ListWorkspacesResponse |
|---|---|
| Коды состояния | 200 400 401 403 404 429 |
| Лимит частоты | 120 в минуту |
POST /workspaces
| Имя | Где | Тип |
|---|---|---|
name | body | string |
clientReference | body | string |
| Ответ | CreatedResponse |
|---|---|
| Коды состояния | 201 400 401 403 404 429 |
| Лимит частоты | 30 в минуту |
DELETE /workspaces/{id}
| Имя | Где | Тип |
|---|---|---|
id обязательный | path | string |
| Ответ | AckResponse |
|---|---|
| Коды состояния | 200 400 401 403 404 429 |
| Лимит частоты | 30 в минуту |
GET /members · POST /members
Чтение требует member:read, приглашение — member:invite. В ответе members и invitations идут по отдельности: приглашение, которое никто не принял, никого не добавило, а слияние этих двух дало бы число мест, расходящееся с тем, за что вам выставляют счёт.
POST отправляет приглашение, а не создаёт учётную запись. Он принимает адрес электронной почты, роль и, необязательно, workspaceIds, чтобы ограничить человека.
Параметры и ответы
GET /members
Объявленных параметров и полей тела нет.
| Ответ | ListMembersResponse |
|---|---|
| Коды состояния | 200 400 401 403 404 429 |
| Лимит частоты | 120 в минуту |
POST /members
| Имя | Где | Тип |
|---|---|---|
email | body | string |
role | body | string |
workspaceIds | body | string[] |
| Ответ | AckResponse |
|---|---|
| Коды состояния | 201 400 401 403 404 429 |
| Лимит частоты | 30 в минуту |
PATCH /members/{id} · DELETE /members/{id} · DELETE /invitations/{id}
Смена роли требует member:update_role, удаление — member:remove, отзыв приглашения — member:invite.
Удаление участника удаляет его личные каналы оповещений — его телефон, его личный чат, его мобильный номер — вместе с его ключами API. Общие каналы не затрагиваются. Последнего владельца нельзя ни удалить, ни понизить.
Параметры и ответы
PATCH /members/{id}
| Имя | Где | Тип |
|---|---|---|
id обязательный | path | string |
role | body | string |
| Ответ | AckResponse |
|---|---|
| Коды состояния | 200 400 401 403 404 429 |
| Лимит частоты | 30 в минуту |
DELETE /members/{id}
| Имя | Где | Тип |
|---|---|---|
id обязательный | path | string |
| Ответ | AckResponse |
|---|---|
| Коды состояния | 200 400 401 403 404 429 |
| Лимит частоты | 30 в минуту |
DELETE /invitations/{id}
| Имя | Где | Тип |
|---|---|---|
id обязательный | path | string |
| Ответ | AckResponse |
|---|---|
| Коды состояния | 200 400 401 403 404 429 |
| Лимит частоты | 30 в минуту |
GET /maintenance · POST /maintenance · DELETE /maintenance
Окна, в течение которых никого не вызывают. По умолчанию проверки продолжают выполняться и записываться, поэтому эти минуты учитываются в доступности наравне с остальными; при keepChecking со значением false во время окна ничего не проверяется, и в истории остаётся пропуск, а не провал. Чтение требует incident:read, запись — maintenance:manage.
Несёт title, startsAt, endsAt, timezone, monitorIds, recurrenceRule, keepChecking, showOnStatusPage и notifySubscribers. Повторяющееся окно — это одна строка, а не много.
Правило повторяет окно в то же местное время в его собственном timezone, так что окно, попавшее на перевод часов, остаётся в запланированный час. FREQ=DAILY, FREQ=WEEKLY и FREQ=MONTHLYразворачиваются, при желании с INTERVAL, COUNT, UNTIL и с BYDAY или BYMONTHDAY, называющим день, в который начинается само окно. Любое другое правило сохраняется и возвращается без изменений и сдерживает оповещения только на первом повторении — оно никогда не угадывается.
Параметры и ответы
GET /maintenance
| Имя | Где | Тип |
|---|---|---|
past | query | boolean |
| Ответ | ListMaintenanceResponse |
|---|---|
| Коды состояния | 200 400 401 403 404 429 |
| Лимит частоты | 120 в минуту |
POST /maintenance
| Имя | Где | Тип |
|---|---|---|
title | body | string |
description | body | string |
workspaceId | body | string |
monitorIds | body | string[] |
startsAt | body | string |
endsAt | body | string |
recurrenceRule | body | string |
timezone | body | string |
keepChecking | body | boolean |
showOnStatusPage | body | boolean |
notifySubscribers | body | boolean |
| Ответ | CreatedResponse |
|---|---|
| Коды состояния | 201 400 401 403 404 429 |
| Лимит частоты | 30 в минуту |
DELETE /maintenance
| Имя | Где | Тип |
|---|---|---|
id | query | string |
| Ответ | AckResponse |
|---|---|
| Коды состояния | 200 400 401 403 404 429 |
| Лимит частоты | 30 в минуту |
GET /on-call
Графики, политики эскалации, замены и тот, кто дежурит прямо сейчас. Требует oncall:read.
onCallNow вычисляется в момент запроса, а viaOverride говорит, пришло это из ротации или от того, кто подменяет. unreachable называет участников, у которых нет канала, способного до них действительно достучаться, — а это та неисправность, которую стоит найти до инцидента, а не во время него.
Параметры и ответы
Объявленных параметров и полей тела нет.
| Ответ | GetOnCallResponse |
|---|---|
| Коды состояния | 200 400 401 403 404 429 |
| Лимит частоты | 120 в минуту |
GET /agents · POST /agents · DELETE /agents · GET /agents/{id}/metrics
Серверные агенты сообщают о CPU, памяти и диске с ваших собственных машин. Регистрация требует agent:enroll, чтение — agent:read, удаление — agent:delete.
Регистрация возвращает токен, один раз. Именно им агент аутентифицируется, поэтому потом прочитать его нельзя.
Параметры и ответы
GET /agents
Объявленных параметров и полей тела нет.
| Ответ | ListAgentsResponse |
|---|---|
| Коды состояния | 200 400 401 403 404 429 |
| Лимит частоты | 120 в минуту |
POST /agents
| Имя | Где | Тип |
|---|---|---|
name | body | string |
workspaceId | body | requests.NullableString |
| Ответ | EnrolledAgentResponse |
|---|---|
| Коды состояния | 201 400 401 403 404 429 |
| Лимит частоты | 30 в минуту |
DELETE /agents
| Имя | Где | Тип |
|---|---|---|
id | query | string |
id | body | string |
| Ответ | AckResponse |
|---|---|
| Коды состояния | 200 400 401 403 404 429 |
| Лимит частоты | 30 в минуту |
GET /agents/{id}/metrics
| Имя | Где | Тип |
|---|---|---|
id обязательный | path | string |
hours | query | integer |
limit | query | integer |
| Ответ | GetAgentMetricsResponse |
|---|---|
| Коды состояния | 200 400 401 403 404 429 |
| Лимит частоты | 120 в минуту |
GET /api-keys · POST /api-keys · DELETE /api-keys
Чтение требует api_key:read, создание и отзыв — api_key:manage. Каждый несёт id, name, prefix, scopes, workspaceIds, owner, createdAt, lastUsedAt, expiresAt и revokedAt.
token возвращается только при создании. prefix — отображаемая часть, и именно она позволяет отличить два ключа в списке, не давая прочитать ни один из них.
Ключ наследует роль и ограничение по рабочим пространствам от того членства, которое его создало, поэтому ключ не может стать обходом ограничения, наложенного на его автора.
Параметры и ответы
GET /api-keys
Объявленных параметров и полей тела нет.
| Ответ | ListApiKeysResponse |
|---|---|
| Коды состояния | 200 400 401 403 404 429 |
| Лимит частоты | 120 в минуту |
POST /api-keys
| Имя | Где | Тип |
|---|---|---|
name | body | string |
scopes | body | string[] |
workspaceIds | body | string[] |
expiresInDays | body | requests.NullableInt64 |
| Ответ | CreatedApiKeyResponse |
|---|---|
| Коды состояния | 201 400 401 403 404 429 |
| Лимит частоты | 30 в минуту |
DELETE /api-keys
| Имя | Где | Тип |
|---|---|---|
id | query | string |
id | body | string |
| Ответ | AckResponse |
|---|---|
| Коды состояния | 200 400 401 403 404 429 |
| Лимит частоты | 30 в минуту |
GET /audit
Изменения конфигурации, с тем, кто их сделал, и тем, что изменилось. Требует audit_log:read — это тариф Business и выше.
Постраничный вывод по nextBefore, а не по номеру страницы, чтобы граница страницы не смещалась, пока вы её читаете. Записи ведутся на любом тарифе — тариф ограничивает чтение, поэтому переход на старший открывает всю историю, а не начинает новую.
Учётные данные, хеши и секреты в changes не записываются никогда.
Параметры и ответы
| Имя | Где | Тип |
|---|---|---|
limit | query | integer |
| Ответ | ListAuditResponse |
|---|---|
| Коды состояния | 200 400 401 403 404 429 |
| Лимит частоты | 120 в минуту |
GET /me
Кто этот ключ, до чего он дотягивается и что позволяет тариф. Прав не требует — описать себя может каждый ключ.
Несёт user, organization, membership(роль и ограничение по рабочим пространствам), entitlements (вычисленные лимиты тарифа, с уже учтёнными дополнительными пакетами) и usage. Используйте его, чтобы проверить лимит до попытки создания, а не после отказа.
Сессия получает ещё и organizations — список всех организаций, в которых состоит человек. Ключ API — нет: ключ выпускается под одно членство и ограничен им, поэтому перечисление остальных рекламировало бы организации, до которых эти учётные данные не дотягиваются.
Параметры и ответы
Объявленных параметров и полей тела нет.
| Ответ | GetMeResponse |
|---|---|
| Коды состояния | 200 400 401 403 404 429 |
| Лимит частоты | 120 в минуту |
DELETE /account
Удаляет организацию и всё, что в ней есть. Константа права эту точку не охраняет, потому что проверка — это владение: всем, кто не владелец, отказано.
Это необратимо и это не пауза. Пользуйтесь осознанно.
Параметры и ответы
Объявленных параметров и полей тела нет.
| Ответ | AccountDeletedResponse |
|---|---|
| Коды состояния | 200 400 401 403 404 429 |
| Лимит частоты | 30 в минуту |
POST /billing/apple/verify
Подтверждает покупку, сделанную внутри нашего собственного мобильного приложения. Требует billing:manage.
Описано ради полноты, а не ради использования: вызвать её можно только с чеком, который Apple выдала нашему приложению, поэтому сторонней интеграции с ней делать нечего. Перечислено здесь потому, что маршрут, который существует и нигде не описан, неотличим от того, который кто-то забыл.
Параметры и ответы
| Имя | Где | Тип |
|---|---|---|
transactionId | body | string |
| Ответ | AppleVerifyResponse |
|---|---|
| Коды состояния | 200 400 401 403 404 429 |
| Лимит частоты | 30 в минуту |
GET /
Индекс. Перечисляет маршруты, которые обслуживает это развёртывание: это машиночитаемая версия этой страницы, и она всегда актуальна — она генерируется из того, что смонтировано на самом деле.
Лента инцидентов (RSS и Atom)
Все инциденты вашего аккаунта в виде ленты — для читалки, а не для клиентской библиотеки. Адрес создаётся в панели, в разделе Настройки → Уведомления:
https://vitrinaengine.com/feeds/incidents/vtf_…/feed.xml
https://vitrinaengine.com/feeds/incidents/vtf_…/atom.xmlАдрес и есть учётные данные. Никаких заголовков отправлять не нужно: кто держит ссылку, тот читает все инциденты аккаунта во всех рабочих пространствах. Адрес всегда можно посмотреть в панели снова, потому что он живёт в читалке, которую кто-нибудь однажды переустановит, — и его можно заменить или отозвать, причём со следующего запроса, а не когда истечёт кэш.
В ленте только факты уровня инцидента: имя монитора, причина, серьёзность, статус, время открытия и закрытия и те сообщения, что помечены публичными. Никогда — разбор, никогда — конфигурация монитора и никогда — место, откуда шла проверка. Большинство читалок синхронизируются через чужие серверы, поэтому документ написан так, будто он покидает ваше здание: он его покидает.
Опрос раз в минуту — норма. Свыше шестидесяти запросов в минуту на один адрес отвечает 429, а адрес, который никогда не был действующим, был заменён или отозван, отвечает 404 — один и тот же ответ во всех трёх случаях, чтобы по нему нельзя было выяснить, какие токены когда-то существовали.
Что этот API намеренно не возвращает
configмонитора. Он может содержать учётные данные.- Из какого региона выполнялась проверка. Откуда мы проверяем — решаем мы, и менять это тоже нам, пока зонды размещаются и переезжают. Это было бы обещание о нашей инфраструктуре, с которым вы всё равно ничего не сделаете, и нигде больше в продукте оно не показывается.
Exporting your data
Two routes hand back a file rather than a message, so that taking your data out does not mean paging through a collection endpoint a thousand times. Both are on every plan, the free one included — what a plan changes is how much history there is to export, not whether you may.
GET /export/{entity}
Один вид строк, в CSV или JSON. entity — одно из monitors, hosts, status-pages, notification-channels, members, incidents, incident-events, postmortems, audit-log, error-issues, uptime-daily, uptime-hourly или analytics-hourly. Всё остальное отвечает 404.
format — csv (по умолчанию) или json. from и to — метки времени RFC 3339. bom добавляет к CSV метку порядка байтов UTF-8: она нужна Excel для имён с не-ASCII символами, а большинство скриптовых языков читают её как часть первого заголовка столбца — поэтому по умолчанию она выключена.
Параметры и ответы
| Имя | Где | Тип |
|---|---|---|
entity обязательный | path | string |
bom | query | boolean |
format | query | string |
from | query | string |
to | query | string |
| Коды состояния | 200 400 401 403 404 429 |
|---|---|
| Лимит частоты | 6 в час |
GET /export
Всё, что может прочитать ключ, одним zip-архивом: по файлу на сущность плюс manifest.json — что внутри, сколько строк в каждом файле и каких сущностей нет. То, что роль ключа читать не может, просто отсутствует, а не ломает весь запрос; манифест не даёт этому произойти незаметно.
Параметры и ответы
| Имя | Где | Тип |
|---|---|---|
bom | query | boolean |
format | query | string |
from | query | string |
to | query | string |
| Коды состояния | 200 400 401 403 404 429 |
|---|---|
| Лимит частоты | 6 в час |
What an export does not contain
Экспорт не содержит ничего, что аутентифицирует: ни настроек каналов, ни токенов heartbeat, ни хешей паролей. Конфигурация монитора включена, но без всего, что похоже на учётные данные — тела запроса, заголовков, SNMP-community, доступа к почтовому ящику. Сырых результатов проверок тоже нет: их удаляет срок хранения, поэтому доступность берётся из агрегатов.
Size, and the one refusal you may meet
Почасовые ряды ограничены 92 днями на запрос; более широкое окно отклоняется с кодом export_window_too_wide, а не урезается молча. Отдельный лимит: 6 экспортов в час на организацию.
Версионирование
Версия указана в пути. В ответы будут добавляться поля — относитесь к незнакомым как к тому, что нужно игнорировать, а не как к ошибке, — но из v1 ничего не будет удалено и ничто не поменяет в ней смысл.
Чего-то не хватает?
Набор возможностей намеренно небольшой: он покрывает настенную панель, сводку в мессенджере и скрипт деплоя, который заглушает монитор на время выката. Если вы делаете что-то, до чего он не дотягивается, напишите на support@vitrinaengine.com — понимание того, что людям действительно нужно, и определяет, какая конечная точка появится следующей.