For AI agents: markdown of this page — /docs-content-en/infra/access-tokens.md documentation index — /llms.txt

Access tokens

Short-lived tokens for external access to a deployed application on a BLACKHOLE server. Two modes: api-bearer — JWT for the Authorization HTTP header, share-url — a distributable link with cookie authentication.

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 — a JWT for the Authorization HTTP header; share-url — a link that sets a cookie when visited.

A development-team key with the ADMIN or DEVELOPER role can mint only api-bearer. On the first request, supply ttlSeconds between 300 and 600. When retrying an approved operation, supply the exact expiresAt from retryOperation and the approved request ID as approvalId, without ttlSeconds. See Approving wider access for the approval flow.

Recovery depends on the calling key. After 410 TOKEN_EXPIRED, the token record may already exist even though no JWT was returned. Use the key that manages the server to list tokens with status=all before another mint. A development-team caller must ask that key holder to reconcile. Development-team keys cannot call refresh or mint share-url; further access requires a separately approved new api-bearer mint.

Parameters

Parameter In Type Required Description
Parameterid Inpath Typestring (UUID) Requiredyes DescriptionBLACKHOLE server ID. List: GET /v1/infra/servers

Request body fields

Field Type Required Default Description
Fieldmode Typestring Requiredyes Default— Description"api-bearer" or "share-url"
FieldttlSeconds Typenumber Requiredno Default86400 DescriptionToken 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"
FieldexpiresAt Typestring (ISO 8601) Requiredno Default— DescriptionOn an approved retry, the exact value from retryOperation, without ttlSeconds
FieldapprovalId Typestring (UUID) Requiredno Default— DescriptionApproved request ID from error.details.proposalId. Retry the original action with the same key and the approved parameters
FieldidentityBound Typeboolean Requiredno Defaulttrue DescriptionFor 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
Fieldname Typestring Requiredno Default— DescriptionToken label for display in the list (up to 100 characters)

Access approval

If minting expands access, the first request returns 409 AUDIENCE_APPROVAL_REQUIRED. Its error.details contains proposalId, approvalUrl, expiresAt and retryOperation. No token is minted at this step. Send the URL to a human authorized to approve access and signed in to their Vibecode account; an API key cannot approve its own request. Use the same key to read the request state. After APPROVED, start with the original HTTP body, remove ttlSeconds, and add only expiresAt from retryOperation and approvalId equal to proposalId. The retryOperation object is not an HTTP body: do not send its internal type or uppercase mode. Do not recalculate expiry or change mode, name or identity binding.

APPROVED means consent, not a minted token. If parameters or access changed, or approval was consumed or expired, branch on the error code in the table below. With 410 TOKEN_EXPIRED, a record may already exist but no JWT was returned: ask the holder of the key that manages the server to list and reconcile token records with status=all before retrying, and obtain separate approval for a further mint. A development-team key cannot list tokens.

Examples

curl — personal key

Terminal
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

Terminal
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": "api-bearer",
    "ttlSeconds": 600,
    "name": "ci-smoke"
  }'

JavaScript — personal key

javascript
const requestBody = { mode: 'api-bearer', ttlSeconds: 600, name: 'ci-smoke' }
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(requestBody),
  }
)
const result = await res.json()
if (!res.ok || result.success !== true) {
  if (res.status === 409 && result.error?.code === 'AUDIENCE_APPROVAL_REQUIRED') {
    const { proposalId, approvalUrl, retryOperation } = result.error.details
    const { ttlSeconds, ...sameIntent } = requestBody
    const retryBody = { ...sameIntent, expiresAt: retryOperation.expiresAt, approvalId: proposalId }
    // Send approvalUrl to a human; after APPROVED, submit retryBody with the same key.
    console.log({ proposalId, approvalUrl, retryBody })
    throw new Error('Access approval is pending')
  }
  throw new Error(result.error?.code ?? `HTTP_${res.status}`)
}
const { data } = result

// 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

javascript
const requestBody = { mode: 'api-bearer', ttlSeconds: 600, name: 'ci-smoke' }
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(requestBody),
  }
)
const result = await res.json()
if (!res.ok || result.success !== true) {
  if (res.status === 409 && result.error?.code === 'AUDIENCE_APPROVAL_REQUIRED') {
    const { proposalId, approvalUrl, retryOperation } = result.error.details
    const { ttlSeconds, ...sameIntent } = requestBody
    const retryBody = { ...sameIntent, expiresAt: retryOperation.expiresAt, approvalId: proposalId }
    // Send approvalUrl to a human; after APPROVED, submit retryBody with the same key.
    console.log({ proposalId, approvalUrl, retryBody })
    throw new Error('Access approval is pending')
  }
  throw new Error(result.error?.code ?? `HTTP_${res.status}`)
}
const { data } = result

// 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

Response fields

The set of fields depends on the mode.

api-bearer mode:

Field Type Description
Fieldsuccess Typeboolean DescriptionAlways true on success
Fielddata.id Typestring DescriptionToken ID for later revocation
Fielddata.mode Typestring Description"api-bearer"
Fielddata.token Typestring DescriptionJWT for the Authorization: Bearer header. Save it right away: this exact string is not returned again, and a fresh JWT for a record issued by the key that manages the server comes from token refresh. A development-team key cannot refresh tokens and needs a separately approved new mint
Fielddata.expiresAt Typestring (ISO 8601) DescriptionRetention period of the token record — for listing and revocation
Fielddata.jwtExpiresAt Typestring (ISO 8601) DescriptionThe actual expiry of the Bearer token. Capped at 10 minutes regardless of ttlSeconds. After it expires, mint a new token
Fielddata.note Typestring DescriptionNote about the difference between expiresAt and jwtExpiresAt
Fielddata.subdomain Typestring DescriptionServer subdomain
Fielddata.appUrl Typestring DescriptionFull HTTPS address of the application
Fielddata.curlExample Typestring DescriptionReady-to-use curl example with the token for a quick check

share-url mode:

Field Type Description
Fieldsuccess Typeboolean DescriptionAlways true on success
Fielddata.id Typestring DescriptionToken ID for later revocation
Fielddata.mode Typestring Description"share-url"
Fielddata.shortcode Typestring DescriptionCode inserted into the URL as ?s=<shortcode>
Fielddata.url Typestring DescriptionThe full shareable link
Fielddata.identityBound Typeboolean DescriptionWhether a visit requires login via Bitrix24
Fielddata.expiresAt Typestring (ISO 8601) DescriptionToken expiration time
Fielddata.name Typestring | null DescriptionThe label passed at mint time

Response example

api-bearer mode:

JSON
{
  "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."
  }
}

share-url mode:

JSON
{
  "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. The count is kept in the database, so the number in the response is exact:

JSON
{
  "success": false,
  "error": {
    "code": "TOKEN_MINT_RATE_LIMIT",
    "message": "Rate limit: 50 mints/hour per API key"
  }
}

Errors

HTTP Code Description
HTTP400 CodeINVALID_MODE DescriptionAn invalid mode was passed, or a field in the body has the wrong value type
HTTP400 CodeUNKNOWN_PARAM DescriptionThe request body contains an unknown field. The response contains details with the list of valid fields and a suggestion
HTTP400 CodeINVALID_TTL DescriptionttlSeconds is outside the allowed range [300, 315,360,000]. A development-team key must supply ttlSeconds between 300 and 600 on the first request, or the approved expiresAt with no more than 600 seconds remaining on retry
HTTP400 CodeNAME_TOO_LONG Descriptionname exceeds 100 characters
HTTP400 CodeSERVER_NO_SUBDOMAIN DescriptionThe server has no subdomain, so there is nothing to send requests to
HTTP401 CodeMISSING_API_KEY DescriptionNo API key was supplied through X-Api-Key or a supported Authorization: Bearer scheme
HTTP401 CodeINVALID_API_KEY DescriptionThe authentication key is not recognized
HTTP401 CodeKEY_INACTIVE DescriptionThe requesting API key is no longer active
HTTP401 CodeKEY_EXPIRED DescriptionThe requesting API key has expired; this is distinct from resource URL expiry
HTTP401 CodeNO_PORTAL DescriptionA non-management key has no portal binding
HTTP401 CodeCOWORK_CHAT_KEY_INACTIVE DescriptionThe parent device key behind a Cowork chat key is no longer usable
HTTP401 CodeWRONG_AUTH_SCHEME DescriptionAn OAuth application key was sent as a Bearer session: put the key in X-Api-Key and the session in Authorization: Bearer
HTTP401 CodeINVALID_SESSION DescriptionThe supplied OAuth session is invalid or expired
HTTP401 CodeTOKEN_EXPIRED DescriptionThe OAuth user token expired and its refresh failed; re-authorization is required
HTTP401 CodeTOKEN_INVALID DescriptionWhen the partner AI plane is enabled, its supplied token is unknown or expired
HTTP401 CodeKEY_REVOKED DescriptionThe partner credential behind the AI plane token is revoked
HTTP402 CodeACCOUNT_FROZEN DescriptionThe key owner’s account is frozen. With wallet-debt scoping disabled, the shared API-key guard refuses this call before its handler. Follow error.topupUrl to resolve the balance; this status alone does not authorize repeating a prior write.
HTTP403 CodeAUDIENCE_APPROVAL_FORBIDDEN DescriptionThe current mint source failed the authority check: for example, the key, its owner or the portal is no longer active, the owner's membership ended, or the approving human lost authority. No token was minted. Restore current authority or use an appropriate key; for an approval, obtain new consent from a human with current authority
HTTP403 CodeFEATURE_DISABLED DescriptionMint passed the preliminary check before the feature was disabled; the fresh transactional check refused it. No token was minted
HTTP403 CodeTOKEN_OWNER_MISMATCH DescriptionThe server belongs to a different API key. An exception is a development-team key with a current ADMIN or DEVELOPER role on this server.
HTTP403 CodeSERVER_KEY_MISMATCH DescriptionWhile the token was being issued, the server moved to another API key — for example, during a key rotation — or was deleted. No token was issued. Retrieve the server again with GET /v1/infra/servers/:id and issue the token with the key that currently owns the server. If the server was deleted, that request returns 404
HTTP403 CodePORTAL_COLLABORATOR_TOKEN_MODE_FORBIDDEN DescriptionA development-team key requested share-url. This key can mint only api-bearer
HTTP403 CodeAGENT_OWNER_ONLY DescriptionThe server was created for an AI agent — access tokens are disabled for it
HTTP403 CodeINFRA_FORBIDDEN_FOR_COWORK_KEY DescriptionThe 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
HTTP403 CodeWRITE_BLOCKED_READONLY_KEY DescriptionA read-only key cannot perform this write.
HTTP403 CodeEXTERNAL_COLLABORATOR_KEY_OUT_OF_SCOPE DescriptionThis operation is outside the external-collaborator key route list. Use the key that manages the server
HTTP403 CodePORTAL_COLLABORATOR_KEY_WRONG_SERVER DescriptionThe development-team key is bound to another server
HTTP403 CodePORTAL_COLLABORATOR_NOT_A_MEMBER DescriptionDevelopment-team membership ended or the current role no longer permits this operation
HTTP403 CodeIP_NOT_ALLOWED DescriptionThe caller IP is outside the key allowlist.
HTTP403 CodeOWNER_BLOCKED DescriptionThe key owner is blocked.
HTTP403 CodeOWNER_DELETED DescriptionThe key owner was deleted.
HTTP403 CodePORTAL_SUSPENDED DescriptionThe bound portal is suspended.
HTTP403 CodePORTAL_DELETED DescriptionThe bound portal was deleted.
HTTP403 CodePORTAL_BLOCKED DescriptionThe bound portal is blocked.
HTTP403 CodeMANAGEMENT_KEY_NO_ENTITY_ACCESS DescriptionThe management key lacks the portal binding or entity authority required by this call.
HTTP403 CodeAGENT_MAINTENANCE_KEY_OUT_OF_SCOPE DescriptionThe maintenance key route list does not include this operation.
HTTP403 CodeAPP_API_KEY_OUT_OF_SCOPE DescriptionThe application key route list does not include this operation.
HTTP403 CodeWAITLIST_PENDING DescriptionThe key owner has not been activated.
HTTP403 CodeSESSION_APP_MISMATCH DescriptionThe OAuth session belongs to another application.
HTTP403 CodePLANE_FORBIDDEN DescriptionThe partner key does not allow this AI plane.
HTTP403 CodeSYSTEM_FORBIDDEN DescriptionThe partner key does not allow this AI system.
HTTP403 CodeSCOPE_FORBIDDEN DescriptionThe partner AI plane forbids this route.
HTTP403 CodeCONNECT_COMPANY_DERIVED_TOKEN_FORBIDDEN DescriptionA company connection key cannot manage derived tokens.
HTTP403 CodeCOWORK_READONLY DescriptionThe Cowork/Code seat is read-only.
HTTP403 CodeGALAXY_DISABLED DescriptionGalaxy operations are disabled for this portal.
HTTP404 CodeNOT_FOUND DescriptionThe server does not exist, was deleted, or exists and belongs to the calling key but its portal has been deactivated; or approvalId was not found or is inaccessible to the calling key
HTTP409 CodeACTIVE_TOKEN_LIMIT DescriptionThe limit of 100 active tokens per server has been reached
HTTP409 CodeKEY_ROTATION_RECOVERY_REQUIRED DescriptionThe replacement of the API key that made the request is unfinished: its rotation ended with a 500 response or has not finished yet. No token was issued. The steps are in Rotate an API key
HTTP409 CodeAUDIENCE_APPROVAL_REQUIRED DescriptionHuman consent is required. Use error.details.proposalId, approvalUrl and retryOperation following the approval flow above
HTTP409 CodeAUDIENCE_APPROVAL_MISMATCH DescriptionThe key, parameters or expected result differs from the approved action. Keep the approved retry unchanged
HTTP409 CodeAUDIENCE_APPROVAL_STALE DescriptionAccess or its revision changed after the request was created. Obtain new approval
HTTP409 CodeAUDIENCE_APPROVAL_REPLAY DescriptionApproval was consumed or is no longer available to apply. Read the request state again
HTTP410 CodeAUDIENCE_APPROVAL_EXPIRED DescriptionApproval expired. Create a new request and obtain consent again
HTTP410 CodeTOKEN_EXPIRED DescriptionThe persisted record expired before its JWT could be signed. No JWT was returned; have the holder of the key that manages the server reconcile token records with status=all before another mint; a development-team key cannot list tokens
HTTP429 CodeQUOTA_EXCEEDED DescriptionAPI billing is enabled with a nonzero overage price: the daily free quota is exhausted and the PREPAY balance is not positive. Top up the balance. error.quota and error.used report counts; X-RateLimit-Quota, X-RateLimit-Used and X-RateLimit-Remaining report the quota, usage and zero remainder. This refusal does not promise Retry-After or X-RateLimit-Scope
HTTP429 CodeCALLER_RATE_LIMITED DescriptionThe shared per-key or per-user request limit was exceeded. Wait for the number of seconds specified in Retry-After; X-RateLimit-Scope identifies the refusing layer
HTTP429 CodeTOKEN_MINT_RATE_LIMIT DescriptionThe limit of 50 mints per hour per API key has been exceeded. The count is kept in the database and is not divided across replicas. Header Retry-After: 3600
HTTP503 CodeFEATURE_DISABLED DescriptionThe access-tokens section is disabled on the platform. The pre-call signal and the fallback are described in Availability
HTTP503 CodeACCOUNT_PENDING_ERASURE DescriptionThe key owner is pending erasure or being erased. Header Retry-After: 3600.
HTTP503 CodeSERVICE_DISABLED DescriptionThe partner AI plane or its quota-readiness service is disabled. No Retry-After is promised for this refusal.
HTTP503 Codeuser_self_deletion_pending DescriptionThe API key owner scheduled self-deletion. The access-tokens check refuses the call before its handler. Header Retry-After: 604800.

For the full list of common API errors, see Errors.

Known specifics

  • Minting a link is recorded in the Bitrix24 account's access log. A successful mint in share-url mode 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 enabled 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, identityBound is false) 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.
  • For tokens issued by the key that manages the server, extending access is cheaper than minting again. Token refresh issues a fresh JWT for the same record and counts against 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. The identityBound field does not affect it and is returned as true in the list response.
  • api-bearer confirms 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 the X-Vibe-Authorization header and does not provide currentUser in the GET /v1/me response. Therefore an application route that verifies a Bitrix24 administrator via X-Vibe-Authorization and GET /v1/me will get currentUser: null under an api-bearer token. Such a route needs full user authorization through an OAuth application (placement) — see What the app receives.

See also