
## Обновить токен доступа

`POST /v1/infra/servers/:id/access-tokens/:tokenId/refresh`

Выпускает свежий JWT для уже существующего токена режима `api-bearer` — **без создания новой записи**. Долгоживущий клиент (CI, AI-агент) вызывает обновление перед истечением `jwtExpiresAt` вместо выпуска нового токена: обновление не расходует ни лимит активных токенов, ни лимит выпусков в час.

Тело запроса не требуется.

## Параметры

| Параметр | В | Тип | Обяз. | Описание |
|----------|---|-----|:-----:|----------|
| `id` | path | string (UUID) | да | ID BLACKHOLE-сервера. Список: [`GET /v1/infra/servers`](/docs/infra/servers/list) |
| `tokenId` | path | string (UUID) | да | ID токена `api-bearer` — поле `data.id` из ответа на [выпуск](./create.md) или элемент [списка токенов](./list.md) |

## Примеры

### curl — личный ключ

```bash
curl -X POST "https://vibecode.bitrix24.tech/v1/infra/servers/SERVER_ID/access-tokens/TOKEN_ID/refresh" \
  -H "X-Api-Key: YOUR_API_KEY"
```

### curl — OAuth-приложение

```bash
curl -X POST "https://vibecode.bitrix24.tech/v1/infra/servers/SERVER_ID/access-tokens/TOKEN_ID/refresh" \
  -H "X-Api-Key: YOUR_APP_KEY" \
  -H "Authorization: Bearer USER_SESSION_TOKEN"
```

### JavaScript — личный ключ

```javascript
const res = await fetch(
  `https://vibecode.bitrix24.tech/v1/infra/servers/${serverId}/access-tokens/${tokenId}/refresh`,
  {
    method: 'POST',
    headers: { 'X-Api-Key': 'YOUR_API_KEY' },
  }
)
const { data } = await res.json()
console.log(data.token, data.jwtExpiresAt)
```

### JavaScript — OAuth-приложение

```javascript
const res = await fetch(
  `https://vibecode.bitrix24.tech/v1/infra/servers/${serverId}/access-tokens/${tokenId}/refresh`,
  {
    method: 'POST',
    headers: {
      'X-Api-Key': 'YOUR_APP_KEY',
      'Authorization': 'Bearer USER_SESSION_TOKEN',
    },
  }
)
const { data } = await res.json()
```

## Поля ответа

| Поле | Тип | Описание |
|------|-----|----------|
| `success` | boolean | Всегда `true` при успехе |
| `data.id` | string | ID токена — **тот же**, что и при выпуске (новая запись не создаётся) |
| `data.mode` | string | `"api-bearer"` |
| `data.token` | string | Свежий JWT для заголовка `Authorization: Bearer` |
| `data.expiresAt` | string (ISO 8601) | Срок хранения записи токена — не меняется при обновлении |
| `data.jwtExpiresAt` | string (ISO 8601) | Реальный срок действия нового JWT. Ограничен 10 минутами (или `expiresAt` записи, если он ближе) |
| `data.subdomain` | string | Субдомен сервера |
| `data.appUrl` | string | Полный HTTPS-адрес приложения |
| `data.curlExample` | string | Готовый `curl`-пример с новым токеном |
| `data.note` | string | Пояснение о сроках действия и о повторном вызове |

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

```json
{
  "success": true,
  "data": {
    "id": "9f1c4b7e-3d52-4a18-9c0e-7b2a1f6d84c3",
    "mode": "api-bearer",
    "token": "eyJhbGciOiJFUzI1NiJ9...",
    "expiresAt": "2026-06-25T10:50:00.000Z",
    "jwtExpiresAt": "2026-06-25T10:40:10.000Z",
    "subdomain": "app-91306a4c",
    "appUrl": "https://app-91306a4c.vibecode.bitrix24.tech",
    "curlExample": "curl -H \"Authorization: Bearer eyJhbGciOiJFUzI1NiJ9...\" https://app-91306a4c.vibecode.bitrix24.tech/api/health",
    "note": "Refreshed the Gateway session JWT for this api-bearer token (same token id, no new row). The JWT is valid for up to 10 minutes (or until the row's expiresAt, whichever is sooner). Call this endpoint again before jwtExpiresAt to keep a long-running client authenticated."
  }
}
```

## Пример ответа при ошибке

410 — срок хранения записи истёк:

```json
{
  "success": false,
  "error": {
    "code": "TOKEN_EXPIRED",
    "message": "The long-lived token row has expired; mint a new token."
  }
}
```

## Ошибки

| HTTP | Код | Описание |
|------|-----|----------|
| 400 | `WRONG_TOKEN_MODE` | Токен в режиме `share-url`. Обновление применимо только к `api-bearer`, ссылки `share-url` обновляются сами при переходе |
| 400 | `SERVER_NO_SUBDOMAIN` | У сервера нет субдомена |
| 401 | `MISSING_API_KEY` | Не передан заголовок `X-Api-Key` |
| 401 | `INVALID_API_KEY` | Неверный или просроченный API-ключ |
| 403 | `TOKEN_OWNER_MISMATCH` | Токен принадлежит другому ключу либо сервер больше не привязан к вашему ключу |
| 403 | `INFRA_FORBIDDEN_FOR_COWORK_KEY` | Вызов сделан ключом Cowork/Code — такой ключ работает только с данными, изменяющие операции ему закрыты. Что делать — [Проектный ключ для деплоя](/docs/cowork/deploy-key) |
| 404 | `NOT_FOUND` | Токен не найден на этом сервере |
| 404 | `SERVER_NOT_FOUND` | Сервер не найден или удалён |
| 410 | `ALREADY_REVOKED` | Токен отозван — выпустите новый |
| 410 | `TOKEN_EXPIRED` | Срок хранения записи истёк — выпустите новый токен через `POST /access-tokens` |
| 503 | `FEATURE_DISABLED` | Раздел токенов доступа выключен на платформе. Признак до вызова и запасной путь — [Доступность](/docs/infra/access-tokens#доступность) |

Полный список общих ошибок API — [Ошибки](/docs/errors).

## Диагностика отказа Gateway

Если запрос к приложению с заголовком `Authorization: Bearer` возвращает `401` с кодом `BH_LOGIN_REQUIRED`, тело ответа содержит поле `reason` с конкретной причиной отказа Gateway:

| `reason` | Что произошло | Что делать |
|----------|---------------|------------|
| `expired` | JWT просрочен (истёк 10-минутный срок) | Обновите токен этим эндпоинтом или выпустите новый |
| `signature` | Подпись не сошлась | Используйте токен, выпущенный для этого сервера, и не редактируйте его |
| `subdomain` | Токен привязан к другому субдомену | Обращайтесь к собственному субдомену `app-*` сервера |
| `revoked` | Токен отозван | Выпустите новый токен |
| `type` | Передан не `api-bearer`-токен | Используйте токен режима `api-bearer`, а не сессию по куки |
| `malformed` / `invalid` | Строка не является корректным JWT | Проверьте целостность токена |

## Известные особенности

- **Один отзыв гасит все выданные JWT токена.** [`DELETE`](./delete.md) по записи прекращает работу и исходного JWT, и всех выпущенных обновлением.
- **Ссылки `share-url` обновляются сами.** При каждом переходе они проходят через `/auth/bh-login`, поэтому вызывать обновление для них не нужно.
- **Готовый цикл «использовал → при 401 обновил → повторил».** Вместо слежения за таймером клиент реагирует на отказ:

```javascript
async function callWithRefresh(serverId, tokenId, appUrl, jwt) {
  let res = await fetch(`${appUrl}/api/health`, {
    headers: { Authorization: `Bearer ${jwt}` },
  })
  if (res.status === 401) {
    const r = await fetch(
      `https://vibecode.bitrix24.tech/v1/infra/servers/${serverId}/access-tokens/${tokenId}/refresh`,
      { method: 'POST', headers: { 'X-Api-Key': 'YOUR_API_KEY' } },
    )
    const { data } = await r.json()
    jwt = data.token // тот же tokenId, свежий JWT
    res = await fetch(`${appUrl}/api/health`, {
      headers: { Authorization: `Bearer ${jwt}` },
    })
  }
  return res
}
```

## Смотрите также

- [Выпустить токен](./create.md)
- [Список токенов](./list.md)
- [Отозвать токен](./delete.md)
- [Токены доступа](/docs/infra/access-tokens)
