For AI agents: markdown of this page — /docs-content-en/infra/access-tokens/create.md documentation index — /llms.txt
Mint an access token
POST /v1/infra/servers/:id/access-tokens
Mints a short-lived token for external access to a deployed application. Two modes: api-bearer — JWT for the Authorization HTTP header; share-url — a link that sets a cookie when visited.
Parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
id |
path | string (UUID) | yes | BLACKHOLE server ID. List: GET /v1/infra/servers |
Request body fields
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
mode |
string | yes | — | "api-bearer" or "share-url" |
ttlSeconds |
number | no | 86400 |
Token lifetime in seconds. Range: 300–315,360,000 (from 5 minutes to 10 years). The value 315,360,000 is shown in the Vibecode dashboard as "No expiration" |
identityBound |
boolean | no | true |
For share-url only. When true, login via Bitrix24 is required and the real user identifier is recorded in the log. When false, the visit is anonymous and a synthetic identifier is recorded |
name |
string | no | — | Token label for display in the list (up to 100 characters) |
Examples
curl — personal key
curl -X POST "https://vibecode.bitrix24.com/v1/infra/servers/SERVER_ID/access-tokens" \
-H "X-Api-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"mode": "api-bearer",
"ttlSeconds": 600,
"name": "ci-smoke"
}'
curl — OAuth application
curl -X POST "https://vibecode.bitrix24.com/v1/infra/servers/SERVER_ID/access-tokens" \
-H "X-Api-Key: YOUR_APP_KEY" \
-H "Authorization: Bearer USER_SESSION_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"mode": "share-url",
"ttlSeconds": 2592000,
"identityBound": false,
"name": "preview"
}'
JavaScript — personal key
const res = await fetch(
`https://vibecode.bitrix24.com/v1/infra/servers/${serverId}/access-tokens`,
{
method: 'POST',
headers: {
'X-Api-Key': 'YOUR_API_KEY',
'Content-Type': 'application/json',
},
body: JSON.stringify({ mode: 'api-bearer', ttlSeconds: 600 }),
}
)
const { data } = await res.json()
// E2E check via the public path
const check = await fetch(`${data.appUrl}/api/health`, {
headers: { Authorization: `Bearer ${data.token}` },
})
console.log(check.status) // 200 — the application responds through the Gateway
JavaScript — OAuth application
const res = await fetch(
`https://vibecode.bitrix24.com/v1/infra/servers/${serverId}/access-tokens`,
{
method: 'POST',
headers: {
'X-Api-Key': 'YOUR_APP_KEY',
'Authorization': 'Bearer USER_SESSION_TOKEN',
'Content-Type': 'application/json',
},
body: JSON.stringify({
mode: 'share-url',
ttlSeconds: 2592000,
name: 'preview',
}),
}
)
const { data } = await res.json()
console.log(data.url) // https://app-xxxx.vibecode.bitrix24.com/?s=R8k3Zm2P
Response fields
The set of fields depends on the mode.
Mode api-bearer:
| Field | Type | Description |
|---|---|---|
success |
boolean | Always true on success |
data.id |
string | Token ID for later revocation |
data.mode |
string | "api-bearer" |
data.token |
string | JWT for the Authorization: Bearer header. Save it right away: this exact string is not returned again, and a fresh JWT for the same record comes from token refresh |
data.expiresAt |
string (ISO 8601) | Retention period of the token record — for listing and revocation |
data.jwtExpiresAt |
string (ISO 8601) | The actual validity period of the Bearer token. Capped at 10 minutes regardless of ttlSeconds. After it expires, mint a new token |
data.note |
string | Note about the difference between expiresAt and jwtExpiresAt |
data.subdomain |
string | Server subdomain |
data.appUrl |
string | Full HTTPS address of the application |
data.curlExample |
string | Ready-to-use curl example with the token for a quick check |
Mode share-url:
| Field | Type | Description |
|---|---|---|
success |
boolean | Always true on success |
data.id |
string | Token ID for later revocation |
data.mode |
string | "share-url" |
data.shortcode |
string | Code inserted into the URL as ?s=<shortcode> |
data.url |
string | The full shareable link |
data.identityBound |
boolean | Whether a visit requires login via Bitrix24 |
data.expiresAt |
string (ISO 8601) | Token expiration time |
data.name |
string | null | The label passed at mint time |
Response example
Mode api-bearer:
{
"success": true,
"data": {
"id": "9f1c4b7e-3d52-4a18-9c0e-7b2a1f6d84c3",
"mode": "api-bearer",
"token": "eyJhbGciOiJFUzI1NiJ9...",
"expiresAt": "2026-05-18T10:50:00.000Z",
"jwtExpiresAt": "2026-05-18T10: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": "JWT is a 10-minute Gateway session token. The row's `expiresAt` is the long-lived TTL for listing/revoking, but the Bearer token itself stops working at `jwtExpiresAt`. To keep a long-running client authenticated, POST /v1/infra/servers/:id/access-tokens/:tokenId/refresh before jwtExpiresAt to re-mint a fresh JWT for the SAME token (no new row — does not consume the active-token cap or mint rate-limit), or use mode=share-url for browser links that auto-refresh on each visit."
}
}
Mode share-url:
{
"success": true,
"data": {
"id": "2a7d5e61-84bc-4f39-b0d7-5e6c9a3f1b28",
"mode": "share-url",
"shortcode": "R8k3Zm2P",
"url": "https://app-91306a4c.vibecode.bitrix24.com/?s=R8k3Zm2P",
"identityBound": false,
"expiresAt": "2026-06-17T08:44:00.000Z",
"name": "preview"
}
}
Error response example
429 — token mint rate limit exceeded:
{
"success": false,
"error": {
"code": "TOKEN_MINT_RATE_LIMIT",
"message": "Rate limit: 50 mints/hour per API key"
}
}
Errors
| HTTP | Code | Description |
|---|---|---|
| 400 | INVALID_MODE |
An invalid mode was passed, or a field in the body has the wrong value type |
| 400 | UNKNOWN_PARAM |
The request body contains an unknown field. The response contains details with the list of valid fields and a suggestion |
| 400 | INVALID_TTL |
ttlSeconds is outside the allowed range [300, 315,360,000] |
| 400 | NAME_TOO_LONG |
name exceeds 100 characters |
| 400 | SERVER_NO_SUBDOMAIN |
The server has no subdomain, so there is nothing to send requests to |
| 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 server belongs to a different API key. Being on the server's development team does not grant access to this operation — it requires the managing key regardless of your role. |
| 403 | AGENT_OWNER_ONLY |
The server was created for an AI agent — access tokens are disabled for it |
| 403 | INFRA_FORBIDDEN_FOR_COWORK_KEY |
The call was made with a Cowork/Code key — such a key works with data only and cannot perform write operations. To issue a key that can, see Project key for deploy |
| 404 | SERVER_NOT_FOUND |
Server not found or deleted |
| 409 | ACTIVE_TOKEN_LIMIT |
The limit of 100 active tokens per server has been reached |
| 429 | TOKEN_MINT_RATE_LIMIT |
The limit of 50 mints per hour per API key has been exceeded. Header Retry-After: 3600 |
| 503 | FEATURE_DISABLED |
The access-tokens section is disabled on the platform. The pre-call signal and the fallback are described in Availability |
Full list of common API errors — Errors.
Known specifics
- Minting a link is recorded in the Bitrix24 account's access log. A successful mint in
share-urlmode is logged when the server is bound to a Bitrix24 account, and does not depend on the account's settings. The administrator can read that log and receive notifications in accounts where the platform has opened the "Applications" section: there the administrator sees that the application was opened via a link, and if the link does not require signing in to Bitrix24 (that is,identityBoundisfalse) and the application had not previously been exposed externally, administrators additionally receive a chat-bot message with a link to the list of externally accessible applications. The request format, the response and the refusal codes do not change. - Extending access is cheaper than minting again. Token refresh issues a fresh JWT for the same record and consumes neither the active-token cap nor the hourly mint limit.
- For
api-bearer, the identifier in the log is always the same. It is the UUID of the API key owner. TheidentityBoundfield does not affect it and is returned astruein the list response. api-bearerconfirms the request is made by the key owner, but does not create a Bitrix24 user session. The token authenticates the request as the API key owner and grants access to the application according to its access policy. It does not inject theX-Vibe-Authorizationheader and does not providecurrentUserin theGET /v1/meresponse. Therefore an application route that verifies a Bitrix24 administrator viaX-Vibe-AuthorizationandGET /v1/mewill getcurrentUser: nullunder anapi-bearertoken. Such a route needs full user authorization through an OAuth application (placement) — see What the app receives.