API управления
Ключи
Выпуск ключей песочницы и серверных ключей, области доступа, ротация, отзыв и удаление — и чем отзыв отличается от удаления.
На этой странице · 7
Ключи живут внутри песочницы. Видов два: sandbox (stend_sbx_…) — им код
интеграции ходит в мок-шлюз, и server (stend_sk_…) — им работают CLI
и Management API.
GET /keys
Область: keys:read. Ключи одной песочницы, свежие сверху.
curl -s "$STEND/api/v1/keys?limit=1&kind=server" -H "Authorization: Bearer $STEND_KEY"
{
"items": [
{
"id": "cmtty1zn80000tqm3zy5prwmf",
"name": "Подрядчик: только логи",
"subtitle": null,
"kind": "server",
"mask": "stend_sk_13ee••••••51d8",
"services": ["bitrix24", "ozon", "wildberries", "apify"],
"scopes": ["logs:read", "sandboxes:read"],
"fullAccess": false,
"status": "active",
"rotationDays": 90,
"requestsPerDay": 0,
"isCurrent": false,
"createdAt": "2026-09-09T10:16:56.756Z",
"lastUsedAt": null,
"expiresAt": null,
"revokedAt": null,
"revokedBy": null
}
],
"nextCursor": "cmtty1zn80000tqm3zy5prwmf"
}
Фильтры: status (active / expiring / revoked), kind (sandbox /
server), плюс общие limit и cursor.
Секрет не отдаётся никогда — только маска: в базе лежит хеш. fullAccess — готовый
признак «серверный ключ без ограничений». Ключ со статусом expiring продолжает
работать; не работает только revoked.
requestsPerDay считает и вызовы Management API
Счётчик пишется через буфер, а не отдельным UPDATE на каждый вызов, и в него
попадают в том числе обращения к /api/v1/*. Это честно: ключ действительно
работал.
POST /keys
Область: keys:write. Ответ — 201, и secret приходит ровно в нём.
Тело запроса
curl -s -X POST "$STEND/api/v1/keys" \
-H "Authorization: Bearer $STEND_KEY" -H 'Content-Type: application/json' \
-d '{"name":"Подрядчик: только логи","kind":"server","scopes":["logs:read","sandboxes:read"]}'
{
"id": "cmtty1zn80000tqm3zy5prwmf",
"name": "Подрядчик: только логи",
"kind": "server",
"mask": "stend_sk_13ee••••••51d8",
"services": ["bitrix24", "ozon", "wildberries", "apify"],
"scopes": ["logs:read", "sandboxes:read"],
"fullAccess": false,
"status": "active",
"rotationDays": 90,
"secret": "stend_sk_13eedc3bfddbce93949c2dd70c8551d8",
"warning": "Сохраните ключ: полностью он показывается только сейчас"
}
rotationDays — срок напоминания о замене, а не срок жизни: сам по себе ключ
не протухает. Ключ создаётся в текущей песочнице; для другой укажите
?sandboxId=.
Ограничения на scopes описаны в разделе
Области доступа: выдать больше, чем есть
у вызывающего, нельзя, а ключу песочницы области не назначаются вовсе (422).
GET и PATCH /keys/{id}
GET (область keys:read) отдаёт тот же объект, что и список. Ключ ищется
только в текущей песочнице: ключ из соседней даёт 404, пока не указан
её ?sandboxId=.
PATCH (область keys:write) меняет name, subtitle, rotationDays
и scopes. Поле services принимается, но не применяется. Секрет остаётся прежним. Пустое тело отвергается:
{ "error": "VALIDATION", "message": "Не указано ни одного поля для изменения" }
POST /keys/{id}/revoke
Область: keys:write. Подтверждение — точное название ключа в confirmName.
Ключ перестаёт работать сразу и навсегда: обратной операции нет, статус active
уже не вернуть. Запись остаётся — она видна в списке со статусом revoked, датой
и именем отозвавшего, а записи журнала запросов сохраняют ссылку на неё.
Ответ — { id, name, status, revokedAt, revokedBy, self, message }. Отозвать
ключ, которым сделан вызов, можно: в ответе это помечено self: true,
и следующий запрос с ним получит 401.
POST /keys/{id}/rotate
Область: keys:write. Ответ — 201 с новым secret.
Старый ключ отзывается в той же транзакции, в которой создаётся новый: прежний секрет перестаёт работать немедленно, без переходного периода. Если он зашит в работающую интеграцию, она встанет до подстановки нового.
Новый ключ наследует название, подпись, вид, сервисы, срок ротации и области
доступа, но получает свой идентификатор; старая запись остаётся в списке
со статусом revoked и пометкой «ротация». В ответе рядом с обычными полями —
replaced с идентификатором, маской и временем отзыва прежнего ключа:
{
"id": "cmttyhq0z000jtqm3cg2lrxe9",
"mask": "stend_sk_ee56••••••5a72",
"secret": "stend_sk_ee56615ed19983ed8194d2a6a77d5a72",
"replaced": {
"id": "cmttyhpxh000itqm3asicdcyf",
"mask": "stend_sk_4561••••••3978",
"revokedAt": "2026-09-09T10:29:10.785Z"
},
"self": false,
"warning": "Сохраните secret: полностью ключ показывается только сейчас. Старый секрет уже не работает"
}
Отозванный ключ не прокручивается — 409:
{
"error": "CONFLICT",
"message": "Ключ отозван 2026-09-09T10:29:10.785Z и прокрутке не подлежит. Выпустите новый через POST /api/v1/keys"
}
Прокрутить чужой ключ с более широкими правами, чем у вызывающего, тоже нельзя: ротация выдаёт на руки рабочий секрет.
DELETE /keys/{id}
Область: keys:write. Подтверждение — confirmName.
curl -s -X DELETE "$STEND/api/v1/keys/$KEY_ID" \
-H "Authorization: Bearer $STEND_KEY" -H 'Content-Type: application/json' \
-d '{"confirmName":"Подрядчик: только логи"}'
{
"id": "cmtty1zn80000tqm3zy5prwmf",
"name": "Подрядчик: только логи",
"ok": true,
"logsDetached": 0,
"tunnelSessionsRemoved": 0,
"self": false,
"message": "Ключ удалён вместе с записью. Восстановить его нельзя, для этого пришлось бы знать секрет"
}
Отзыв или удаление
Удаляйте, когда мешает мусор; отзывайте, когда важен след.
Подводные камни
- Секрет показывается один раз — при создании и при ротации. Забытый ключ заменяется ротацией, а не восстановлением.
servicesключа песочницы больше ничего не ограничивают: ключ открывает все сервисы стенда. Поле осталось в ответах, чтобы не ломать клиентов, и всегда содержит полный набор. Области доступа (scopes) — другое: они про Management API и работают как прежде.- Отзыв, ротация и правка областей действуют мгновенно: кеш разбора ключей сбрасывается сразу, а не через полминуты.