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Такой записи для вас нет. См. ниже.
413Source 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 map300 в час

Сверх лимита возвращается 429 с заголовком Retry-After, где указано целое число секунд до сброса окна. Соблюдайте его, а не повторяйте запрос сразу.

Они заданы там, где обычная интеграция до них не доходит: настенная панель, опрашивающая раз в десять секунд, тратит шесть из 120 чтений. На всех тарифах они одинаковы, потому что коллекционные конечные точки возвращают всё одним ответом — аккаунту с пятьюстами мониторами не нужно больше запросов, чем аккаунту с двадцатью. У source map почасовой лимит, потому что они приходят пачкой во время деплоя, где у фронтенда легко бывает сотня фрагментов.

404 вместо 403, намеренно

На идентификатор, принадлежащий другому клиенту, приходит «Not found.», а не «Forbidden.» Ответ 403 подтвердил бы, что запись существует, и превратил бы конечную точку в способ проверять, настоящий ли идентификатор. Не читайте 404 как доказательство того, что нигде ничего нет, — только того, что нет ничего, что этот ключ вправе видеть.

Конечные точки

Всё в аккаунте, что совпадает с ?q=: мониторы, инциденты и их разборы, серверы, страницы статуса, рабочие пространства, каналы уведомлений, окна обслуживания, проекты ошибок и проблемы, сайты аналитики, участники и приглашения, ключи API, агенты и частные зонды. Один запрос вместо запроса на каждый тип.

?type= сужает выборку до списка типов через запятую, а ?limit= ограничивает число результатов каждого типа от 1 до 20 (по умолчанию 5). Каждый тип требует того же разрешения, что и его собственная точка доступа; тип, который ваш ключ не может читать, отсутствует так же, как тип без совпадений. Ничего запечатанного, хешированного или секретного не ищется и не возвращается.

Параметры и ответы
ИмяГдеТип
limitqueryinteger
qquerystring
typequerystring
Ответ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 не возвращается никогда. Для некоторых видов он содержит заголовки запроса и учётные данные, и ключ с правом только на чтение не должен быть способом прочитать обратно секреты, которые кто-то ввёл в форму.

Параметры и ответы
ИмяГдеТип
tagquerystring
Ответ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 — прочитайте обратно, если это важно.

Параметры и ответы
ИмяГдеТип
namebodystring
kindbodystring
workspaceIdbodystring
configbodystring
intervalSecondsbodyinteger
confirmationsbodyinteger
dependsOnbodystring[]
probeIdbodystring
tagsbodystring[]
Ответ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 обязательныйpathstring
Ответ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 обязательныйpathstring
namebodystring
intervalSecondsbodyinteger
confirmationsbodyinteger
enabledbodyboolean
pausedbodyboolean
configbodystring
tagsbodyStringList
ОтветUpdatedMonitorResponse
Коды состояния200 400 401 403 404 429
Лимит частоты30 в минуту

DELETE /monitors/{id}

Требует monitor:delete. Отвечает { "data": { "id": "…", "deleted": true } }.

Параметры и ответы
ИмяГдеТип
id обязательныйpathstring
Ответ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

ИмяГдеТип
namebodystring
providerbodystring
addressbodystring
notesbodystring
ОтветCreatedResponse
Коды состояния201 400 401 403 404 429
Лимит частоты30 в минуту

GET /hosts/{id} · PATCH /hosts/{id} · DELETE /hosts/{id}

Подробности содержат сервер и его мониторы, каждый с флагом pinned, а если он не установлен — с alsoOn: другие машины, с которых монитор отвечает. Именно это читают перед перезагрузкой. DELETE выводит сервер из списка, и все его мониторы продолжают работать: у сервера нет ни своего расписания, ни своей истории.

Параметры и ответы

GET /hosts/{id}

ИмяГдеТип
id обязательныйpathstring
ОтветGetHostResponse
Коды состояния200 400 401 403 404 429
Лимит частоты120 в минуту

PATCH /hosts/{id}

ИмяГдеТип
id обязательныйpathstring
namebodystring
providerbodystring
addressbodystring
notesbodystring
ОтветAckResponse
Коды состояния200 400 401 403 404 429
Лимит частоты30 в минуту

DELETE /hosts/{id}

ИмяГдеТип
id обязательныйpathstring
ОтветAckResponse
Коды состояния200 400 401 403 404 429
Лимит частоты30 в минуту

POST /hosts/{id}/monitors · DELETE /hosts/{id}/monitors

Один монитор на сервер или с сервера — через monitorId в теле или ?monitorId=. Намеренно не целый список: в агентском аккаунте на сервере живут мониторы нескольких рабочих пространств, и тот, кто заменил бы «список», заменил бы строки, которых ему не показывали. Обе операции идемпотентны.

Параметры и ответы

POST /hosts/{id}/monitors

ИмяГдеТип
id обязательныйpathstring
monitorIdbodystring
ОтветAckResponse
Коды состояния200 400 401 403 404 429
Лимит частоты30 в минуту

DELETE /hosts/{id}/monitors

ИмяГдеТип
id обязательныйpathstring
monitorIdquerystring
monitorIdbodystring
ОтветAckResponse
Коды состояния200 400 401 403 404 429
Лимит частоты30 в минуту

GET /monitors/{id}/hosts · PUT /monitors/{id}/hosts

Обратная сторона той же связи, и здесь это именно целый список: связи принадлежат одному монитору, поэтому на другом конце нет ничего, что видело бы другое рабочее пространство. Отправьте hostIds; пустой массив — настоящий запрос и означает, что монитор не привязан ни к какому серверу. Чужие идентификаторы отбрасываются, а не отклоняются. В каждом ответе о мониторе есть также hosts — пустой, если серверов нет.

Параметры и ответы

GET /monitors/{id}/hosts

ИмяГдеТип
id обязательныйpathstring
ОтветListMonitorHostsResponse
Коды состояния200 400 401 403 404 429
Лимит частоты120 в минуту

PUT /monitors/{id}/hosts

ИмяГдеТип
id обязательныйpathstring
hostIdsbodystring[]
Ответ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 — то поле, на которое стоит смотреть, когда вы строите ленту оповещений. Если оно заполнено, этот инцидент входит в зону влияния другого: упал узел с базой данных, а это один из двенадцати сервисов за ним. Пропустите такие — и получите одно оповещение вместо тринадцати.

Параметры и ответы
ИмяГдеТип
limitqueryinteger
openqueryboolean
statusquerystring
ОтветListIncidentsResponse
Коды состояния200 400 401 403 404 429
Лимит частоты120 в минуту

GET /incidents/{id}

Один инцидент. Требует incident:read. Те же поля, что и в списке, плюс workspaceId.

Он не возвращает хронологию инцидента — комментарии и смены статуса, видные в панели, на этой конечной точке отсутствуют. Если они вам нужны, скажите, и их можно добавить; описывать их здесь, пока их нет, было бы хуже, чем этот пробел.

Параметры и ответы
ИмяГдеТип
id обязательныйpathstring
Ответ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 обязательныйpathstring
statusbodystring
commentbodystring
publishbodyboolean
Ответ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 обязательныйpathstring
bodybodystring
Ответ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 обязательныйpathstring
Ответ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

ИмяГдеТип
organizationIdbodystring
Ответ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

ИмяГдеТип
phonebodystring
namebodystring
ОтветSmsEnrolmentResponse
Коды состояния201 400 401 403 404 429
Лимит частоты30 в минуту

POST /sms/enrolment/confirm

ИмяГдеТип
codebodystring
ОтветSmsConfirmedResponse
Коды состояния200 400 401 403 404 429
Лимит частоты30 в минуту

POST /channels/{id}/confirmation

Повторно отправляет письмо подтверждения для email-канала. Требуется notification_channel:manage. Каждый вызов выпускает новую ссылку, а прежняя перестаёт работать. sent: false означает, что отправлять было нечего — адрес уже подтверждён, — и подтверждённому адресу этим путём ничего повторно не пишут. Если почтовый провайдер отказал в отправке, ответ — 502 с confirmation_not_sent.

Параметры и ответы
ИмяГдеТип
id обязательныйpathstring
ОтветChannelConfirmationResponse
Коды состояния200 400 401 403 404 429
Лимит частоты30 в минуту

PATCH /workspaces/{id}

Переименовывает рабочее пространство. Требуется workspace:update. name обязателен (от 1 до 80 символов), clientReference необязателен — null его очищает. Slug не меняется никогда, потому что входит в URL страниц статуса. В ответе — рабочее пространство в новом виде.

Параметры и ответы
ИмяГдеТип
id обязательныйpathstring
namebodystring
clientReferencebodyrequests.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 и имени файла.

Карты разбираются при чтении проблемы, а не при загрузке, поэтому карта, загруженная уже после того, как ошибки пришли, всё равно помогает — и это обычный порядок.

Параметры и ответы
ИмяГдеТип
projectRefbodyinteger
filenamebodystring
debugIdbodystring
releasebodystring
mapbodystring
mapGzipBase64bodystring
Ответ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 — двадцать четыре почасовых значения, от старых к новым.

Параметры и ответы
ИмяГдеТип
limitqueryinteger
projectquerystring
qquerystring
statusquerystring
ОтветListIssuesResponse
Коды состояния200 400 401 403 404 429
Лимит частоты120 в минуту

GET /errors/issues/{id}

Одна проблема со своим стеком. Требует monitor:read. exceptions — это собственная цепочка SDK: брошенная ошибка последней, её причины перед ней, — и её кадры разбираются по любой загруженной вами source map: тот же разбор, что делает панель, чтобы скрипт и человек, с которым вы говорите, никогда не смотрели на разные стеки. Карта, которую применить не удалось, откатывается к исходным кадрам, а не роняет запрос.

lastEvent несёт две отметки времени намеренно. occurredAt — то, что сообщил SDK, а receivedAt — когда приём записал событие; устройство, бывшее офлайн, или очередь, которая накопилась, — это ровно промежуток между ними.

Параметры и ответы
ИмяГдеТип
id обязательныйpathstring
ОтветGetIssueResponse
Коды состояния200 400 401 403 404 429
Лимит частоты120 в минуту

PATCH /errors/issues/{id}

Решить, игнорировать или открыть заново: { "status": "resolved" }. Требует monitor:update. ignoreHours может сопровождать ignored, чтобы позже снять игнорирование, и ограничен 90 днями.

Решение записывает release, в котором проблема была решена, поэтому запоздавшее событие из более старого развёртывания её не переоткрывает. Когда SDK не прислал release, сравнивать не с чем, и любое последующее событие её всё же переоткроет: раз отличить опоздавшего от регрессии нельзя, безопасно считать, что ошибка вернулась.

Параметры и ответы
ИмяГдеТип
id обязательныйpathstring
statusbodystring
ignoreHoursbodyinteger
Ответ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 обязательныйpathstring
daysqueryinteger
Ответ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 обязательныйpathstring
daysqueryinteger
Ответ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 обязательныйpathstring
hoursqueryinteger
ОтветGetMonitorGraphResponse
Коды состояния200 400 401 403 404 429
Лимит частоты120 в минуту

GET /monitors/graphs

ИмяГдеТип
hoursqueryinteger
Ответ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 обязательныйpathstring
ОтветGetMonitorChannelsResponse
Коды состояния200 400 401 403 404 429
Лимит частоты120 в минуту

PUT /monitors/{id}/channels

ИмяГдеТип
id обязательныйpathstring
channelIdsbodyStringList
ОтветGetMonitorChannelsResponse
Коды состояния200 400 401 403 404 429
Лимит частоты30 в минуту

GET /incidents/{id}/events

Хронология одного инцидента: смены состояния, подтверждения, комментарии, шаги эскалации. Требует incident:read. Это та самая хронология, которой GET /incidents/{id} не отдаёт.

Параметры и ответы
ИмяГдеТип
id обязательныйpathstring
Ответ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

ИмяГдеТип
kindbodystring
namebodystring
targetbodystring
workspaceIdbodyrequests.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 обязательныйpathstring
enabledbodyboolean
ОтветAckResponse
Коды состояния200 400 401 403 404 429
Лимит частоты30 в минуту

DELETE /channels/{id}

ИмяГдеТип
id обязательныйpathstring
ОтветAckResponse
Коды состояния200 400 401 403 404 429
Лимит частоты30 в минуту

POST /channels/{id}/test

ИмяГдеТип
id обязательныйpathstring
Ответ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

ИмяГдеТип
namebodystring
workspaceIdbodystring
Ответ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 обязательныйpathstring
ОтветGetStatusPageResponse
Коды состояния200 400 401 403 404 429
Лимит частоты120 в минуту

PATCH /status-pages/{id}

ИмяГдеТип
id обязательныйpathstring
namebodystring
headlinebodyrequests.NullableString
descriptionbodyrequests.NullableString
logoUrlbodyrequests.NullableString
themeAccentbodystring
languagebodystring
historyDaysbodyinteger
showResponseTimesbodyboolean
showIncidentHistorybodyboolean
subscribersEnabledbodyboolean
visibilitybodystring
passwordbodyrequests.NullableString
customCssbodyrequests.NullableString
hideVitrinaBrandingbodyboolean
groupByHostbodyboolean
ОтветUpdatedStatusPageResponse
Коды состояния200 400 401 403 404 429
Лимит частоты30 в минуту

DELETE /status-pages/{id}

ИмяГдеТип
id обязательныйpathstring
Ответ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 обязательныйpathstring
monitorIdbodystring
displayNamebodystring
ОтветComponentAckResponse
Коды состояния201 400 401 403 404 429
Лимит частоты30 в минуту

DELETE /status-pages/{id}/components

ИмяГдеТип
id обязательныйpathstring
componentquerystring
componentIdbodystring
Ответ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

ИмяГдеТип
namebodystring
clientReferencebodystring
ОтветCreatedResponse
Коды состояния201 400 401 403 404 429
Лимит частоты30 в минуту

DELETE /workspaces/{id}

ИмяГдеТип
id обязательныйpathstring
Ответ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

ИмяГдеТип
emailbodystring
rolebodystring
workspaceIdsbodystring[]
Ответ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 обязательныйpathstring
rolebodystring
ОтветAckResponse
Коды состояния200 400 401 403 404 429
Лимит частоты30 в минуту

DELETE /members/{id}

ИмяГдеТип
id обязательныйpathstring
ОтветAckResponse
Коды состояния200 400 401 403 404 429
Лимит частоты30 в минуту

DELETE /invitations/{id}

ИмяГдеТип
id обязательныйpathstring
Ответ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

ИмяГдеТип
pastqueryboolean
ОтветListMaintenanceResponse
Коды состояния200 400 401 403 404 429
Лимит частоты120 в минуту

POST /maintenance

ИмяГдеТип
titlebodystring
descriptionbodystring
workspaceIdbodystring
monitorIdsbodystring[]
startsAtbodystring
endsAtbodystring
recurrenceRulebodystring
timezonebodystring
keepCheckingbodyboolean
showOnStatusPagebodyboolean
notifySubscribersbodyboolean
ОтветCreatedResponse
Коды состояния201 400 401 403 404 429
Лимит частоты30 в минуту

DELETE /maintenance

ИмяГдеТип
idquerystring
Ответ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

ИмяГдеТип
namebodystring
workspaceIdbodyrequests.NullableString
ОтветEnrolledAgentResponse
Коды состояния201 400 401 403 404 429
Лимит частоты30 в минуту

DELETE /agents

ИмяГдеТип
idquerystring
idbodystring
ОтветAckResponse
Коды состояния200 400 401 403 404 429
Лимит частоты30 в минуту

GET /agents/{id}/metrics

ИмяГдеТип
id обязательныйpathstring
hoursqueryinteger
limitqueryinteger
Ответ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

ИмяГдеТип
namebodystring
scopesbodystring[]
workspaceIdsbodystring[]
expiresInDaysbodyrequests.NullableInt64
ОтветCreatedApiKeyResponse
Коды состояния201 400 401 403 404 429
Лимит частоты30 в минуту

DELETE /api-keys

ИмяГдеТип
idquerystring
idbodystring
ОтветAckResponse
Коды состояния200 400 401 403 404 429
Лимит частоты30 в минуту

GET /audit

Изменения конфигурации, с тем, кто их сделал, и тем, что изменилось. Требует audit_log:read — это тариф Business и выше.

Постраничный вывод по nextBefore, а не по номеру страницы, чтобы граница страницы не смещалась, пока вы её читаете. Записи ведутся на любом тарифе — тариф ограничивает чтение, поэтому переход на старший открывает всю историю, а не начинает новую.

Учётные данные, хеши и секреты в changes не записываются никогда.

Параметры и ответы
ИмяГдеТип
limitqueryinteger
Ответ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 выдала нашему приложению, поэтому сторонней интеграции с ней делать нечего. Перечислено здесь потому, что маршрут, который существует и нигде не описан, неотличим от того, который кто-то забыл.

Параметры и ответы
ИмяГдеТип
transactionIdbodystring
Ответ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 обязательныйpathstring
bomqueryboolean
formatquerystring
fromquerystring
toquerystring
Коды состояния200 400 401 403 404 429
Лимит частоты6 в час

GET /export

Всё, что может прочитать ключ, одним zip-архивом: по файлу на сущность плюс manifest.json — что внутри, сколько строк в каждом файле и каких сущностей нет. То, что роль ключа читать не может, просто отсутствует, а не ломает весь запрос; манифест не даёт этому произойти незаметно.

Параметры и ответы
ИмяГдеТип
bomqueryboolean
formatquerystring
fromquerystring
toquerystring
Коды состояния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 — понимание того, что людям действительно нужно, и определяет, какая конечная точка появится следующей.

Тарифы · Условия использования