For AI agents: markdown of this page — /docs-content-en/infra/access-tokens/refresh.md documentation index — /llms.txt
Refresh an access token
POST /v1/infra/servers/:id/access-tokens/:tokenId/refresh
Mints a fresh JWT for an existing api-bearer token — without creating a new record. The JWT is valid for up to 10 minutes (or until the record's expiresAt, whichever is sooner). A long-running client (CI, AI agent) should call refresh before jwtExpiresAt expires rather than minting a new token: refresh does not consume the active-token limit or the hourly mint limit.
No request body is required.
Parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
id |
path | string (UUID) | yes | BLACKHOLE server ID |
tokenId |
path | string (UUID) | yes | api-bearer token ID (the data.id field from the mint response) |
Examples
curl
curl -X POST "https://vibecode.bitrix24.com/v1/infra/servers/SERVER_ID/access-tokens/TOKEN_ID/refresh" \
-H "X-Api-Key: YOUR_API_KEY"
JavaScript — "use → on 401 refresh → retry" loop
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.com/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 // same tokenId, fresh JWT
res = await fetch(`${appUrl}/api/health`, {
headers: { Authorization: `Bearer ${jwt}` },
})
}
return res
}
Response fields
| Field | Type | Description |
|---|---|---|
success |
boolean | Always true on success |
data.id |
string | Token ID — the same as at mint time (no new record is created) |
data.mode |
string | "api-bearer" |
data.token |
string | Fresh JWT for the Authorization: Bearer header |
data.expiresAt |
string (ISO 8601) | Retention period of the token record — unchanged by refresh |
data.jwtExpiresAt |
string (ISO 8601) | Real expiry of the new JWT. Capped at 10 minutes (or the record's expiresAt, whichever is sooner) |
data.subdomain |
string | Server subdomain |
data.appUrl |
string | Full HTTPS address of the app |
data.curlExample |
string | Ready-to-use curl example with the new token |
data.note |
string | Explanation of validity and re-calling |
Response example
{
"success": true,
"data": {
"id": "tk_7d3fa2b1",
"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.com",
"curlExample": "curl -H \"Authorization: Bearer eyJhbGciOiJFUzI1NiJ9...\" https://app-91306a4c.vibecode.bitrix24.com/api/health",
"note": "Refreshed the Gateway session JWT for this api-bearer token (same token id, no new row)."
}
}
Errors
| HTTP | Code | Description |
|---|---|---|
| 400 | WRONG_TOKEN_MODE |
The token is in share-url mode. Refresh applies only to api-bearer; share-url links auto-refresh on each visit |
| 400 | SERVER_NO_SUBDOMAIN |
The server has no subdomain |
| 401 | MISSING_API_KEY |
The X-Api-Key header was not provided |
| 401 | INVALID_API_KEY |
Invalid or expired API key |
| 403 | TOKEN_OWNER_MISMATCH |
The token belongs to a different key, or the server is no longer bound to your key |
| 404 | NOT_FOUND |
Token not found on this server |
| 404 | SERVER_NOT_FOUND |
Server not found or deleted |
| 410 | ALREADY_REVOKED |
The token was revoked — mint a new one |
| 410 | TOKEN_EXPIRED |
The record's retention period has expired — mint a new token via POST /access-tokens |
| 503 | FEATURE_DISABLED |
Access tokens are disabled on the platform |
Full list of common API errors — Errors.
Diagnosing a Gateway rejection
If a request to the app with the Authorization: Bearer header returns 401 with code BH_LOGIN_REQUIRED, the response body carries a reason field with the specific Gateway rejection cause:
reason |
What happened | What to do |
|---|---|---|
expired |
The JWT expired (the 10-minute window elapsed) | Refresh the token via this endpoint, or mint a new one |
signature |
The signature did not verify | Use a token minted for this server; do not edit it |
subdomain |
The token is bound to a different subdomain | Call the server's own app-* subdomain |
revoked |
The token was revoked | Mint a new token |
type |
A non-api-bearer token was sent |
Use an api-bearer token, not a cookie session |
malformed / invalid |
The string is not a valid JWT | Check the token is intact |
Known specifics
- Refresh creates no new record.
data.idstays the same; the token record and itsexpiresAtdo not change — only the JWT is renewed. A single revoke (DELETE) kills the original and all refreshed JWTs of one token. - A JWT never outlives its record. If less than 10 minutes remain until the record's
expiresAt, the newjwtExpiresAtis capped to that time. api-beareronly.share-urllinks refresh automatically on each visit via/auth/bh-login.