REST API
Заводите мониторы тем же деплоем, что и сами задачи.
Монитор, заведённый руками, живёт отдельно от задачи, которую сторожит. Через полгода половина мониторов следит за задачами, которых уже нет, а половина новых задач не прикрыта ничем. Лечится это одним: монитор описывается рядом с задачей и создаётся тем же деплоем.
Аутентификация
Ключ выдаётся в настройках воркспейса и показывается один раз: мы храним только его хэш. Передаётся заголовком Authorization.
curl -s https://tickwatch.dev/api/v1/monitors \
-H "Authorization: Bearer $TICKWATCH_TOKEN"У ключа два уровня доступа: read и write. По умолчанию выдаётся только чтение — ключ, который лежит в CI и умеет удалять мониторы, должен быть осознанным решением, а не значением по умолчанию.
Ограничения
Лимит запросов считается на воркспейс и зависит от тарифа: от 60 запросов в минуту на бесплатном до 1200 на Business. Текущее состояние возвращается заголовками X-RateLimit-Limit, X-RateLimit-Remaining и X-RateLimit-Reset — на них и стоит смотреть, а не подбирать паузы вслепую.
Ошибки
{
"error": {
"code": "invalid_request",
"message": "Некорректное cron-выражение",
"field": "cron_expr"
}
}- 401 unauthorized — ключа нет, он отозван или истёк
- 403 forbidden — ключу не хватает доступа write
- 404 not_found — объекта нет либо он принадлежит другому воркспейсу
- 402 limit_reached — исчерпан лимит тарифа
- 422 invalid_request — тело запроса не прошло проверку
- 429 rate_limited — слишком много запросов
Создать монитор
curl -s -X POST https://tickwatch.dev/api/v1/monitors \
-H "Authorization: Bearer $TICKWATCH_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"kind": "cron",
"cron_expr": "0 3 * * *",
"name": "Ночной бэкап",
"tz": "Europe/Moscow",
"grace_sec": 600,
"max_duration_sec": 3600
}'В ответе — созданный монитор вместе с ping_url. Это и есть смысл вызова: адрес нужен немедленно, чтобы подставить его в саму задачу, а не ходить за ним вторым запросом.
{
"id": "9d0f…",
"name": "Ночной бэкап",
"state": "new",
"schedule": {
"kind": "cron",
"cron_expr": "0 3 * * *",
"interval_sec": null,
"tz": "Europe/Moscow",
"grace_sec": 600,
"max_duration_sec": 3600
},
"ping_url": "https://ping.tickwatch.dev/4eca85c1-…",
"last_ping_at": null,
"created_at": "2026-08-23T01:38:28.918Z"
}Список и постраничность
curl -s "https://tickwatch.dev/api/v1/monitors?limit=50" \
-H "Authorization: Bearer $TICKWATCH_TOKEN"
# Следующая страница — по курсору из next_cursor.
curl -s "https://tickwatch.dev/api/v1/monitors?limit=50&cursor=MjAyNi0wOC0…" \
-H "Authorization: Bearer $TICKWATCH_TOKEN"Пагинация курсорная, а не по смещению. Список мониторов меняется под читающим, и при offset клиент, листающий страницы, пропускает записи и видит дубли.
Изменить и удалить
# Поставить на паузу на время планового переезда.
curl -s -X PATCH https://tickwatch.dev/api/v1/monitors/$ID \
-H "Authorization: Bearer $TICKWATCH_TOKEN" \
-H "Content-Type: application/json" \
-d '{"paused": true}'
# Сменить расписание.
curl -s -X PATCH https://tickwatch.dev/api/v1/monitors/$ID \
-H "Authorization: Bearer $TICKWATCH_TOKEN" \
-H "Content-Type: application/json" \
-d '{"kind": "interval", "interval_sec": 900}'
curl -s -X DELETE https://tickwatch.dev/api/v1/monitors/$ID \
-H "Authorization: Bearer $TICKWATCH_TOKEN"PATCH меняет только переданные поля, но расписание проверяется целиком: недостающие части берутся из текущего состояния монитора. Неизвестные поля отвергаются с 422 — молча проглоченная опечатка в имени поля означала бы монитор с расписанием, о котором автор скрипта не думал.
Запуски и инциденты
# Последние запуски с кодами возврата и хвостом вывода.
curl -s "https://tickwatch.dev/api/v1/monitors/$ID/runs?limit=20" \
-H "Authorization: Bearer $TICKWATCH_TOKEN"
# Что сломано прямо сейчас.
curl -s "https://tickwatch.dev/api/v1/incidents?status=open" \
-H "Authorization: Bearer $TICKWATCH_TOKEN"
# Отметить, что дежурный уже разбирается.
curl -s -X POST https://tickwatch.dev/api/v1/incidents/$INCIDENT/ack \
-H "Authorization: Bearer $TICKWATCH_TOKEN"Отметка не закрывает инцидент: закрыть его может только успешный пинг от самой задачи. Она нужна, чтобы остальные не бросались на ту же аварию, и повторный вызов время первой отметки не сдвигает.
Пример: провижининг вместе с деплоем
#!/usr/bin/env bash
set -euo pipefail
API="https://tickwatch.dev/api/v1"
AUTH="Authorization: Bearer $TICKWATCH_TOKEN"
NAME="nightly-report"
# Ищем монитор по имени среди существующих.
existing=$(curl -fsS "$API/monitors?limit=200" -H "$AUTH" \
| jq -r --arg n "$NAME" '.data[] | select(.name == $n) | .id')
if [ -z "$existing" ]; then
created=$(curl -fsS -X POST "$API/monitors" -H "$AUTH" \
-H 'Content-Type: application/json' \
-d "{\"kind\":\"cron\",\"cron_expr\":\"0 3 * * *\",\"name\":\"$NAME\"}")
ping=$(echo "$created" | jq -r '.ping_url')
else
ping=$(curl -fsS "$API/monitors/$existing" -H "$AUTH" | jq -r '.ping_url')
fi
# Подставляем адрес в саму задачу — тем же деплоем.
kubectl create secret generic tickwatch \
--from-literal=ping-url="$ping" \
--dry-run=client -o yaml | kubectl apply -f -