#Infrastructure
Create and manage virtual servers for deploying Bitrix24 applications. Each server is invisible from the internet by default (Black Hole mode) — the application is reachable only through the HTTPS subdomain app-{id}.vibecode.bitrix24.tech. Server management and deployment go through the REST API without SSH.
Scope: vibe:infra (added automatically to every API key) · Base URL: https://vibecode.bitrix24.tech/v1 · Authorization: X-Api-Key header
#Documentation sections
- Providers and catalogs — list of providers, plans, regions, and OS images (4 endpoints).
- Servers — create, list, details, delete (4 endpoints).
- Lifecycle — start, stop, sleep, wake, tunnel repair, provisioning status (9 endpoints).
- Access and modes — access policy, user/department list, SSH credentials, BLACKHOLE↔OPEN mode (7 endpoints).
- Deploy API — run commands, upload files, deploy the application, logs, port, metrics, lock, runtimes (8 endpoints).
- Access tokens — short-lived tokens for e2e checks and shareable links (3 endpoints).
- What arrives at the application — Gateway injection of
X-Vibe-Authorization: Bearer, reading identity via/v1/me, handler skeletons in Node/Python/Go. - Portal event subscriptions — delivery of Bitrix24 events (
ONTASKADDand similar) to the app through the tunnel, without polling (3 endpoints).
#What to know up front
- The application port is always 3000. The Black Hole tunnel proxies exactly this port; you don't need to change it. The server is an isolated environment:
:3000inside the virtual machine has no relation to the ports on your local machine. - Deploy API is BLACKHOLE-only. All of
/deploy,/exec,/upload,/logsrequire servers inBLACKHOLEmode with the tunnel agent in statusCONNECTED. For OPEN servers they return an error. /deployand/execreturn JSON by default. This is safe for AI agents and MCP clients — no extra query parameters are needed. If you genuinely need a streaming response (live deploy-step output in the UI), pass?stream=true— then you get SSE (Server-Sent Events). The documentation used to claim the opposite (SSE by default,?stream=falsefor JSON) — that is outdated and no longer matches the API behavior.accessPolicyis security. Changing the policy fromOWNER_ONLYtoPORTAL/AUTHENTICATED/PUBLICopens the application to other users. Never changeaccessPolicywithout explicit user confirmation.- The server is a clean Ubuntu 24.04 with root access enabled. The virtual machine is created from a stock Ubuntu image with no preinstalled software (except the tunnel agent). The agent runs as the
rootuser — nosudois needed inpreStart,install, or/execcommands. Outbound internet is unrestricted:apt-get,curl,wget,pipwork directly. Inbound traffic is blocked except the tunnel connection. - The Bitrix24 plan plays a dual role. First, the Bitrix24 REST API itself is available only on commercial portal plans — without it neither applications, nor the
/v1/dealsproxy, nor bots, nor any other call proxied into Bitrix24 work. Second, on top of that — creating servers, deploying, and waking require an active BitrixGPT + Marketplace subscription on the portal. AI Router works independently of the Bitrix24 plan — it does not proxy into REST and is available even on free plans (BYOK free; platform models are billed against the VibeCode balance). Details are in the "Plan and access" section below. - User authorization inside the application. On every request the Gateway injects six headers prefixed with
X-Vibe-:Request-Idalways, plusUser-Id,User-Name,User-Role,Portal-Id,Authorization(Bearer vibe_session_<…>) for an authenticated request. The browser never sees or stores theAuthorizationtoken — it lives only between the Gateway and the app server. For a quick user identifier theX-Vibe-User-Idheader is enough; the full context (scopes,capabilities, plan, application info) comes from a singleGET /v1/mecall with server-side caching. The user ID in the/v1/meresponse isdata.currentUser.bitrixUserId, the portal domain isdata.portal. The full header table, the BFF pattern, and handler skeletons in Node/Python/Go — What arrives at the application. - Creating servers requires a user session for
vibe_app_keys.POST /v1/infra/serversis gated by a plan check that needs to know exactly who is creating the server. Forvibe_api_the user context already lives in the key itself; forvibe_app_anAuthorization: Bearer <session>is required — without it the response is401 UNAUTHENTICATEDwith anerror.hintpointing at the OAuth flow. Reads and the Deploy API on already-existing servers don't require a session — the "Endpoint authorization" table below collects all the rules in one place.
#Quick start
Three calls — create a server and run the application.
#curl — personal key
export VIBE_KEY="YOUR_API_KEY"
# 1. Create a server (automatically in Black Hole mode)
curl -X POST https://vibecode.bitrix24.tech/v1/infra/servers \
-H "X-Api-Key: $VIBE_KEY" \
-H "Content-Type: application/json" \
-d '{
"provider": "bitrix-cloud",
"name": "my-app",
"plan": "bc-small",
"region": "ru-central1-b",
"image": "fd83esfomhq25p2ono90"
}'
# 2. Wait until ready: status=running AND blackholeStatus=CONNECTED
curl -H "X-Api-Key: $VIBE_KEY" \
https://vibecode.bitrix24.tech/v1/infra/servers/SERVER_ID
# 3. Deploy the application (?stream=false — JSON instead of SSE)
curl -X POST "https://vibecode.bitrix24.tech/v1/infra/servers/SERVER_ID/deploy?stream=false" \
-H "X-Api-Key: $VIBE_KEY" \
-H "Content-Type: application/json" \
-d '{
"source": { "url": "https://github.com/user/app/archive/main.tar.gz" },
"runtime": "node20",
"install": "cd /opt/app && npm install --production",
"start": "cd /opt/app && node server.js",
"port": 3000
}'
The application is reachable at https://app-{id}.vibecode.bitrix24.tech — the appUrl field in the /deploy response.
#curl — OAuth application
# Same thing, just add the Authorization: Bearer header with the session token
curl -X POST https://vibecode.bitrix24.tech/v1/infra/servers \
-H "X-Api-Key: YOUR_APP_KEY" \
-H "Authorization: Bearer USER_SESSION_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "provider": "bitrix-cloud", "name": "my-app", "plan": "bc-small", "region": "ru-central1-b", "image": "fd83esfomhq25p2ono90" }'
#Full example
A realistic JavaScript scenario — create a server, wait until ready, deploy, get the application URL.
const VIBE_KEY = process.env.VIBE_KEY
const BASE = 'https://vibecode.bitrix24.tech/v1'
async function api(method, path, body = null) {
const opts = { method, headers: { 'X-Api-Key': VIBE_KEY } }
if (body) {
opts.headers['Content-Type'] = 'application/json'
opts.body = JSON.stringify(body)
}
const res = await fetch(`${BASE}${path}`, opts)
if (!res.ok) throw new Error(`${method} ${path} → ${res.status}`)
return res.json()
}
// 1. Pick provider, plan, region, image
const { data: plans } = await api('GET', '/infra/providers/bitrix-cloud/plans')
const { data: regions } = await api('GET', '/infra/providers/bitrix-cloud/regions')
const { data: images } = await api('GET', '/infra/providers/bitrix-cloud/images')
const plan = plans.find(p => p.id === 'bc-small')
const region = regions.find(r => r.id === 'ru-central1-b')
const image = images[0]
// 2. Create a server (always in Black Hole)
const { data: server } = await api('POST', '/infra/servers', {
provider: 'bitrix-cloud',
name: 'my-crm-bot',
plan: plan.id,
region: region.id,
image: image.id,
})
console.log(`Server created: ${server.id}, subdomain: ${server.subdomain}`)
// 3. Wait until status becomes running AND blackholeStatus becomes CONNECTED
let info = server
while (info.status !== 'running' || info.blackholeStatus !== 'CONNECTED') {
await new Promise(r => setTimeout(r, 10000)) // 10 seconds between polls
const res = await api('GET', `/infra/servers/${server.id}`)
info = res.data
console.log(`status=${info.status}, blackhole=${info.blackholeStatus}`)
}
// 4. Deploy the application (?stream=false is required for a JSON response)
const deploy = await api('POST', `/infra/servers/${server.id}/deploy?stream=false`, {
source: { url: 'https://github.com/user/app/archive/main.tar.gz' },
runtime: 'node20',
install: 'cd /opt/app && npm install --production',
preStart: 'cd /opt/app && npx prisma migrate deploy',
start: 'cd /opt/app && node server.js',
port: 3000,
env: { NODE_ENV: 'production' },
})
console.log(`Application is live: ${deploy.data.appUrl}`)
// 5. (Optional) Configure auto-sleep after 60 minutes of inactivity
await api('PATCH', `/infra/servers/${server.id}/sleep`, { sleepAfterMinutes: 60 })
#Endpoint reference
All 35 endpoints. Links lead to pages with parameters, examples, and error codes.
Providers and catalogs:
| Method | Path | Description |
|---|---|---|
| GET | `/v1/infra/providers` | List of cloud providers |
| GET | `/v1/infra/providers/:providerId/plans` | Provider plans |
| GET | `/v1/infra/providers/:providerId/regions` | Provider regions |
| GET | `/v1/infra/providers/:providerId/images` | OS images |
Servers:
| Method | Path | Description |
|---|---|---|
| POST | `/v1/infra/servers` | Create a server (always Black Hole) |
| GET | `/v1/infra/servers` | List of your servers |
| GET | `/v1/infra/servers/:id` | Server details |
| DELETE | `/v1/infra/servers/:id` | Delete a server |
Lifecycle:
| Method | Path | Description |
|---|---|---|
| POST | `/v1/infra/servers/:id/start` | Start a stopped/sleeping server |
| POST | `/v1/infra/servers/:id/stop` | Stop a running server |
| POST | `/v1/infra/servers/:id/reboot` | Reboot the server |
| POST | `/v1/infra/servers/:id/wake` | Wake a sleeping server (async or blocking) |
| POST | `/v1/infra/servers/:id/sleep-now` | Immediately put a BLACKHOLE server to sleep |
| PATCH | `/v1/infra/servers/:id/sleep` | Configure auto-sleep |
| POST | `/v1/infra/servers/:id/refresh` | Request status and IP from the provider |
| POST | `/v1/infra/servers/:id/repair` | Restore the tunnel via serial console |
| GET | `/v1/infra/servers/:id/repair-status` | Tunnel restore progress |
Access and modes:
| Method | Path | Description |
|---|---|---|
| GET | `/v1/infra/servers/:id/ssh` | SSH credentials (OPEN only) |
| PATCH | `/v1/infra/servers/:id/mode` | Switch BLACKHOLE↔OPEN |
| PATCH | `/v1/infra/servers/:id/access-policy` | Application access policy |
| GET | `/v1/infra/servers/:id/access` | List of users and departments with access |
| POST | `/v1/infra/servers/:id/access` | Add a user or department |
| DELETE | `/v1/infra/servers/:id/access/:accessId` | Remove an access entry |
| GET | `/v1/infra/servers/:id/b24-users` | Search Bitrix24 portal users |
Deploy API:
| Method | Path | Description |
|---|---|---|
| POST | `/v1/infra/servers/:id/exec` | Run a command (SSE or JSON) |
| POST | `/v1/infra/servers/:id/upload` | Upload a file (base64 or by URL) |
| GET | `/v1/infra/servers/:id/logs` | Service logs (journalctl utility) |
| POST | `/v1/infra/servers/:id/deploy` | Full application deploy |
| PATCH | `/v1/infra/servers/:id/port` | Set the application port |
| GET | `/v1/infra/servers/:id/metrics` | Tunnel activity metrics |
| DELETE | `/v1/infra/servers/:id/lock` | Release a stuck operation lock |
| GET | `/v1/infra/runtimes` | List of available runtimes |
Access tokens:
| Method | Path | Description |
|---|---|---|
| POST | `/v1/infra/servers/:id/access-tokens` | Issue an access token (api-bearer or share-url) |
| GET | `/v1/infra/servers/:id/access-tokens` | List of server tokens |
| DELETE | `/v1/infra/servers/:id/access-tokens/:tokenId` | Revoke a token |
Portal event subscriptions:
| Method | Path | Description |
|---|---|---|
| POST | `/v1/infra/servers/:id/event-subscriptions` | Subscribe the server to a portal event (event.bind under the OAuth app) |
| GET | `/v1/infra/servers/:id/event-subscriptions` | List subscriptions + recent deliveries |
| DELETE | `/v1/infra/servers/:id/event-subscriptions/:subId` | Remove a subscription |
#Endpoint authorization
All infra endpoints require the X-Api-Key header. For vibe_app_ keys (bound to an OAuth application) some POST operations additionally require Authorization: Bearer <session> — without it the response is 401 UNAUTHENTICATED with an error.hint. For vibe_api_ keys the user context already lives in the key itself, so a separate session is not needed.
| Endpoint | X-Api-Key |
Authorization: Bearer for vibe_app_ |
When Bearer is required |
|---|---|---|---|
GET /v1/infra/providers/* |
yes | no | — |
GET /v1/infra/servers, GET /v1/infra/servers/:id |
yes | no | — |
GET /v1/infra/servers/:id/logs, /metrics, /access, /access-tokens, /b24-users, /ssh |
yes | no | — |
GET /v1/infra/runtimes |
yes | no | — |
POST /v1/infra/servers (create a server) |
yes | yes | Plan gate: the platform needs to know exactly who is creating the server. |
POST /v1/infra/servers/:id/deploy, /exec, /upload |
yes | no | The vibe:infra scope on the key is enough. |
POST /v1/infra/servers/:id/start, /stop, /reboot, /wake, /sleep-now, /refresh, /repair, PATCH /sleep |
yes | no | — |
PATCH /v1/infra/servers/:id/mode, /access-policy, /port |
yes | no | — |
POST /v1/infra/servers/:id/access, DELETE /v1/infra/servers/:id/access/:accessId |
yes | no | — |
POST /v1/infra/servers/:id/access-tokens, DELETE .../:tokenId |
yes | no | The platform flag accessTokensEnabled must be enabled; otherwise 503 FEATURE_DISABLED. |
POST/GET/DELETE /v1/infra/servers/:id/event-subscriptions |
yes | no | The server must be backed by an OAuth app with an application_token; otherwise 400 NOT_OAUTH_APP. |
DELETE /v1/infra/servers/:id |
yes | no | — |
DELETE /v1/infra/servers/:id/lock |
yes | no | — |
Quick check before calling: GET /v1/me → data.capabilities.servers.create.available. For vibe_app_ without a session it returns false with reason: "SESSION_REQUIRED" and a hint in userMessage — the model immediately sees that it needs to go through the OAuth flow rather than hitting a 401 on the POST /v1/infra/servers itself.
#Limits
| Limit | Value |
|---|---|
| Servers per API key | 3 |
| Deploy API operations per minute per server | 10 |
Concurrent exec/deploy per server |
1 |
exec timeout |
1–600 seconds (default 300) |
base64 body size (upload inline, source.content) |
500 MB |
File size via source.url / upload url |
500 MB |
multipart archive size in deploy |
500 MB |
/ssh request rate |
up to 10 per minute |
The platform rate limit is shared across all V1 endpoints, see the "Limits and optimization" section.
#Server statuses
| Status | Description |
|---|---|
provisioning |
The virtual machine is being created at the provider (1–3 minutes) |
running |
The virtual machine is running, IP assigned. For the tunnel you also need blackholeStatus: CONNECTED |
sleeping |
Stopped by the sleep timer or manually. Wakes automatically on a request to the HTTPS subdomain or a /deploy//start//wake call |
error |
The server is not in a working state: the virtual machine was deleted at the provider, the agent hasn't connected for a long time, the external externalId is missing |
deleted |
The server is deleted (soft-delete). Cannot be restored |
The blackholeStatus field describes the agent tunnel state independently of status:
| Value | Description |
|---|---|
NONE |
Right after server creation, before the agent's first connection attempt |
WAITING |
The agent is preparing to connect |
CONNECTED |
The tunnel is active, the Deploy API is available |
DISCONNECTED |
The agent was connected, now there is no link — try `/repair` |
The runtimeStatus field is deprecated, kept for compatibility. For servers created after 2026-04-25 (when the runtime parameter was removed from `POST /v1/infra/servers`) it always returns null. The runtime is now installed at the `POST /:id/deploy` stage, and the readiness signal is the success of the runtime step itself in the deploy response.
#Plan and access
VibeCode infrastructure has two levels of access conditions.
Level 1 — Bitrix24 REST API. Bitrix24 itself opens the REST API only on commercial portal plans. This is not about VibeCode: on free Bitrix24 plans it simply does not return REST responses. So without a commercial Bitrix24 plan the following do not work:
- Creating and publishing applications (
POST /api/apps) — registered on the portal via REST. - REST proxy:
/v1/deals,/v1/contacts,/v1/batch,/v1/bots,/v1/tasksand all other entities. - Bots, chats, tasks — everything proxied into Bitrix24.
Level 2 — VibeCode infrastructure. On top of the first condition, creating servers, deploying, and waking require an active BitrixGPT + Marketplace subscription on the portal:
POST /v1/infra/servers— creating a server.POST /v1/infra/servers/:id/deploy— deploying the application.POST /v1/infra/servers/:id/wakeand automatic waking whenpreventWake=true.- Creating agents and managed bots (they provision servers under the hood).
What works on any Bitrix24 plan, including free:
- AI Router —
POST /v1/chat/completions,POST /v1/audio/transcriptions,GET /v1/models. Does not proxy into Bitrix24, goes directly to LLM providers. With BYOK keys — free; with platform models — billed against the VibeCode balance. - Basic platform endpoints:
GET /v1/me,GET /v1/feedback,GET /v1/guide— for AI-agent self-orientation.
Check before calling: GET /v1/me → the capabilities.servers.create.available field. If false — the capabilities.servers.create.userMessage field contains a translated explanation for the user.
Force refresh after an upgrade: GET /v1/me?refresh=tariff — skips the cache (1 hour by default) and makes a fresh app.info request.
Access to servers: the single signal is capabilities.servers.create in the GET /v1/me response (see above). If access is closed, POST /v1/infra/servers returns 402 (or 403 for REGION_NOT_SUPPORTED) with the access-gate code (see "Error codes" below). Access is governed by the BitrixGPT + Marketplace subscription on the portal.
Response headers of the infra endpoints:
| Header | Value |
|---|---|
X-Tariff-Checked-At |
ISO timestamp of the last reconciliation via app.info (max 1 hour of cache) |
X-Tariff-Is-Commercial |
"true" or "false" |
The access-gate error codes are listed in the "Error codes" section below.
#Error codes
#Infrastructure errors
| Code | HTTP | Description |
|---|---|---|
NOT_FOUND |
404 | Server not found or belongs to a different API key |
INVALID_REQUEST |
400 | Validation error (invalid name, plan, region, image) |
INFRA_NOT_PERMITTED |
403 | Infrastructure disabled on the platform or on the portal |
SERVER_CREATION_DISABLED |
403 | Server creation forbidden by portal policy |
MAX_SERVERS_REACHED |
403 | The limit of 3 servers per API key is exceeded |
NO_CREDENTIALS |
404 | The provider is not configured on the platform |
SERVER_NOT_READY |
409 | The server is still being created, the operation is not available yet |
CONFLICT |
409 | The server is in a status from which the action cannot be performed (e.g. start on a running one) |
PROVIDER_ERROR |
502 | The cloud provider returned an error |
VM_MISSING |
422 | The record has no externalId — the virtual machine was not created at the provider or was deleted externally. Delete the server via DELETE and create a new one |
PORT_RESTRICTED |
400 | Port 1–1023 (system ports forbidden). Allowed: 0 (auto-detect) and 1024–65535 |
BLACKHOLE_ONLY |
400 | The endpoint works only for BLACKHOLE servers (applies to /sleep-now, /sleep, /metrics) |
OPEN_MODE_NOT_ALLOWED |
403 | Switching to OPEN is forbidden by the portal policy allowOpenMode |
SAME_MODE |
400 | The server is already in the requested mode |
NOT_IMPLEMENTED |
501 | The action is not supported by the provider (e.g. /reboot on some plugins) |
REPAIR_BLOCKED |
409 | Repair is blocked (preventWake=true or the server is deleted) |
#Deploy API errors
| Code | HTTP | Description |
|---|---|---|
AGENT_NOT_CONNECTED |
409 | The tunnel agent is not in status CONNECTED |
EXEC_BUSY |
409 | Another operation is already running on the server. Use `/lock` to release a stuck lock |
EXEC_TIMEOUT |
504 | Execution timeout exceeded |
EXEC_FAILED |
500 | Command execution error on the agent |
UPLOAD_PATH_DENIED |
403 | Forbidden upload path |
DEPLOY_FAILED |
500 | Error during the deploy process (see the step field in the response) |
VALIDATION_ERROR |
400 | Malformed Deploy API request body |
#Access-gate and billing errors
| Code | HTTP | Description |
|---|---|---|
MARKETPLACE_REQUIRED |
402 | The portal has no active BitrixGPT + Marketplace subscription — subscribe to unlock server creation, deployment, and waking |
KZ_PAID_ONLY |
402 | In Kazakhstan the BitrixGPT + Marketplace subscription is available only on a paid plan (no demo access) |
REGION_NOT_SUPPORTED |
403 | The BitrixGPT + Marketplace subscription is not yet available in the portal's region |
COMMERCIAL_PLAN_REQUIRED |
402 | Free Bitrix24 plan without an active BitrixGPT + Marketplace subscription |
TRIAL_PORTAL_LIMIT |
402 | The per-portal server limit for RU/BY demo access is exceeded (1 server per portal) |
PLAN_NOT_ALLOWED_ON_TRIAL |
402 | The requested plan is not available on RU/BY demo access (only bc-micro is allowed) |
ACCOUNT_FROZEN |
402 | The VibeCode balance is frozen. A top-up is required |
BILLING_EXHAUSTED |
402 | The VibeCode balance is exhausted. Waking and deployment are blocked |
SERVER_WAKE_BLOCKED |
403 | Waking is blocked (not due to billing: administrative block, security) |
#System errors
| Code | HTTP | Description |
|---|---|---|
MISSING_API_KEY |
401 | The X-Api-Key header was not provided |
INVALID_API_KEY |
401 | Invalid or expired API key |
SCOPE_DENIED |
403 | The key does not have the vibe:infra scope |
RATE_LIMIT_EXCEEDED |
429 | Rate limit exceeded |
INTERNAL_ERROR |
500 | Internal server error |
The full reference of common errors — Errors.
#See also
- API overview — overall platform architecture.
- Limits and optimization — rate limits, auto-pagination, batch requests.
- API account (`/v1/me`) — plan and capability check.
- What arrives at the application — Gateway injection of
X-Vibe-Authorization, BFF pattern, handler skeletons.