For AI agents: markdown of this page — /docs-content-en/infra/servers/delete.md documentation index — /llms.txt
Delete a server
DELETE /v1/infra/servers/:id
Deletes a server irreversibly: the virtual machine is destroyed at the provider, the database record is marked as DELETED, and open billing transactions are finalized. A deleted server cannot be recovered — create a new one via POST /v1/infra/servers. Deletion is idempotent only for the virtual machine on the provider side: if it has already been deleted there, no error is returned. A repeated call for a server that is already marked deleted returns 404.
Parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
id |
path | string (UUID) | yes | Server ID from POST /v1/infra/servers or GET /v1/infra/servers |
Examples
curl — personal key
curl -X DELETE -H "X-Api-Key: YOUR_API_KEY" \
https://vibecode.bitrix24.com/v1/infra/servers/db008c84-91a5-4e15-b9d5-6c6aa2838448
curl — OAuth application
curl -X DELETE -H "X-Api-Key: YOUR_APP_KEY" \
-H "Authorization: Bearer USER_SESSION_TOKEN" \
https://vibecode.bitrix24.com/v1/infra/servers/SERVER_ID
JavaScript — personal key
const res = await fetch(
`https://vibecode.bitrix24.com/v1/infra/servers/${serverId}`,
{
method: 'DELETE',
headers: { 'X-Api-Key': 'YOUR_API_KEY' },
}
)
const { success } = await res.json()
if (success) console.log('Server deleted')
JavaScript — OAuth application
const res = await fetch(
`https://vibecode.bitrix24.com/v1/infra/servers/${serverId}`,
{
method: 'DELETE',
headers: {
'X-Api-Key': 'YOUR_APP_KEY',
'Authorization': 'Bearer USER_SESSION_TOKEN',
},
}
)
Response fields
| Field | Type | Description |
|---|---|---|
success |
boolean | true on successful deletion |
The response is short — the body { "success": true } confirms the deletion. No additional data is returned.
Response example
{
"success": true
}
Error response example
404 — the server is already deleted or does not exist:
{
"success": false,
"error": {
"code": "NOT_FOUND",
"message": "Server not found"
}
}
Errors
| HTTP | Code | Description |
|---|---|---|
| 401 | MISSING_API_KEY |
The X-Api-Key header was not provided |
| 401 | INVALID_API_KEY |
Invalid or expired API key |
| 404 | NOT_FOUND |
Server not found, belongs to another API key, or was already deleted |
| 409 | GALAXY_HAS_APPS |
The server is a Galaxy host that still has non-deleted applications. Delete the applications first, then the host. The body contains appCount |
| 502 | GALAXY_HOST_UNREACHABLE |
A Galaxy application: its host is unreachable and could not be woken. The response carries error.hint with a recovery plan — see "Known specifics" |
| 429 | RATE_LIMITED |
The platform's overall request limit was exceeded |
The full list of common API errors — Errors.
Known specifics
- Handling 404 on retries is normal. If the client lost the connection after the first
DELETE, a retry will return 404. Catch 404 as "already deleted". - If the virtual machine has already been deleted at the provider by another means (the provider's panel, manual cleanup) —
DELETEstill succeeds and marks the database record as deleted. - The quota is freed immediately. After deletion you can create a new server right away —
MAX_SERVERS_REACHEDwill no longer count this record. - Deletion works for any status. Servers in
provisioning,running,sleeping, anderrorare all deleted normally. - A Galaxy application is deleted by this same method. When a Bitrix24 account places applications in galaxies (several applications run as containers on a shared host),
DELETE /v1/infra/servers/:idremoves the application the key owns: it tears down its container on the host and marks the record deleted. The response is200 { "success": true }, the same as for a regular server. - The platform wakes a sleeping Galaxy host on its own. If the host of a Galaxy application is asleep, the platform wakes it before tearing down the container — there is no need to wake another application and retry the request. The error 502
GALAXY_HOST_UNREACHABLEremains only when the host is unreachable or could not be woken, for example when it is frozen for billing. In that case retry the request shortly. - The
502 GALAXY_HOST_UNREACHABLEresponse has anerror.hintfield with a recovery plan. It is an object of four strings:reason— why the host is unreachable right now,recovery— what to do,recoveryAction— the concrete call to retry,note— a caveat that the host status in the listing can lag behind the real tunnel state. The point of the hint: the server record is preserved until the container teardown actually runs on the host, so retrying the request in 1–2 minutes loses nothing. If the condition holds beyond 15 minutes, the host is genuinely down. - A Galaxy host with non-deleted applications is not deleted by this method. If the server is a Galaxy host that still has non-deleted applications, the request returns 409
GALAXY_HAS_APPSwith anappCountfield. Delete the host's applications first, then the host itself.
See also
- Create a server —
POST /v1/infra/servers. - List servers —
GET /v1/infra/servers(deleted ones are not included in the output). - Get a server —
GET /v1/infra/servers/:id. - Stop a server — an alternative to deletion, if you want to return to the server later.
- Put a server to sleep — to save money on a temporarily unused BLACKHOLE server.
Create a server
POST /v1/infra/servers
Creates an application with a cloud provider. Always in Black Hole mode — iptables blocks all inbound ports, the application is invisible from the internet.
Your Bitrix24 account has two placement models, and what to expect after creation depends on the model:
- A dedicated virtual machine (the default model). The response is returned immediately with the
provisioningstatus. Provisioning takes 1–3 minutes, after which you need to pollGET /v1/infra/servers/:iduntilstatus: "running"andblackholeStatus: "CONNECTED". The response contains the SSH datassh.passwordandssh.privateKeyonly once — they will be needed when switching to OPEN mode, so save them right away. This page describes that model in the request fields, response fields, and error table below. - A Galaxy application — a container on a shared host. If your Bitrix24 account places apps in galaxies, the same
POST /v1/infra/serverscreates a Galaxy application instead of a virtual machine. In the response, this model is marked bycreatedVia: "galaxy". The "Galaxy application" section below describes the two startup scenarios, and the full model is on the Galaxy app page.
Galaxy application
If your Bitrix24 account places apps in galaxies, POST /v1/infra/servers creates a Galaxy application — a container on a shared host rather than a dedicated virtual machine. The request and the call order are the same, but the lifecycle differs. In the response, this model is marked by createdVia: "galaxy". The full model, cost, and deploy differences are on the Galaxy app page.
Do not wait for CONNECTED before uploading the code. A Galaxy application never reaches blackholeStatus: "CONNECTED" on its own — its container is built on code upload. If you just poll the status, the app stays in provisioning, and after about 20 minutes the platform marks it as error and records in the provisionError field that the code was never uploaded. So upload the code right away — using one of the two scenarios below.
Scenario 1 — transparent, recommended
A single request. Pass the application code in the source field of POST /v1/infra/servers together with runtime and start (and, if needed, install, env, port). The build runs in the background. After the response, poll GET /v1/infra/servers/:id until status: "running". A separate code-upload call is not needed.
curl -X POST https://vibecode.bitrix24.com/v1/infra/servers \
-H "X-Api-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "my-crm-app",
"displayName": "My CRM bot",
"source": { "content": "<base64-archive>" },
"runtime": "node20",
"start": "node index.js",
"install": "npm ci",
"port": 3000
}'
The source, runtime, start, install, env, port fields have the same meaning as in the POST /:id/deploy body:
| Field | Type | Required | Description |
|---|---|---|---|
source |
object | yes (for this scenario) | The code source. source.content — the application archive in base64, up to 500 MB per request body |
runtime |
string | yes (if source is passed) |
Runtime ID: node20, python311, php83, static, and others. The list — GET /v1/infra/runtimes |
start |
string | yes (if source is passed) |
The application start command, on a single line. A line break will return 400 |
install |
string | no | The dependency-install command, on a single line |
env |
object | no | Environment variables { "KEY": "value" }. Injected into the container at start, see below |
port |
number | no | The port the application listens on |
Without the runtime and start fields, the source field returns 400 — both fields are required. On a Bitrix24 account without Galaxy mode, source is forbidden and returns 400 SOURCE_AT_CREATE_GALAXY_ONLY.
Scenario 2 — two-step
Create the application without source — the response comes with the provisioning status and the hint next: "deploy". Immediately call POST /v1/infra/servers/:id/deploy with the code in source, the runtime, and the start command. Upload the code right away, without waiting for CONNECTED.
# Step 1 — create the application without code
curl -X POST https://vibecode.bitrix24.com/v1/infra/servers \
-H "X-Api-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "provider": "bitrix-cloud", "name": "my-crm-app", "plan": "bc-medium", "region": "bc-eu-central" }'
# The response contains data.id, data.next = "deploy", and data.hint.
# Step 2 — upload the code right away, without waiting for CONNECTED
curl -X POST https://vibecode.bitrix24.com/v1/infra/servers/SERVER_ID/deploy \
-H "X-Api-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"source": { "content": "<base64-archive>" },
"runtime": "node20",
"start": "node index.js",
"port": 3000
}'
provider, plan and region are required by the schema for a create without source; for a galaxy app the values are informational — the app inherits its host's plan and region.
Diagnostics and environment variables
- The
provisionErrorfield. If the build or the startup failed,GET /v1/infra/servers/:idreturns the failure reason indata.provisionError, and on an image-build failure — also the tail of the Docker build log. Read this field to find the cause. envvariables are injected at start. Values fromenvare passed into the container at start time viadocker run --env, not baked into the image. So API keys and secrets fromenvdo not end up in the image layers.
Request fields (body)
| Field | Type | Required | Description |
|---|---|---|---|
provider |
string | yes | Provider ID from GET /v1/infra/providers, for example bitrix-cloud. 1–50 characters |
name |
string | yes | System name of the server — the technical identifier. 2–63 characters, only lowercase Latin letters, digits, and -, with a letter as the first character. Pattern: ^[a-z][a-z0-9-]*$. Used in the subdomain URL (app-<hex> is generated separately), logs, the audit log. Immutable after creation. For a human-readable name, use displayName |
displayName |
string | no | Human-readable name in any language (Russian, Chinese, emoji — anything). 2–100 characters, no control bytes. Displayed in the UI, server-down notifications, security alerts, billing lines, the Bitrix24 catalog. If not passed — name is substituted. The name itself remains the technical identifier, immutable, and is used in the subdomain URL and logs |
description |
string | no | Application description shown on the Bitrix24 catalog card, up to 500 characters. Line breaks and tabs are allowed, other control bytes are rejected. The value is trimmed at both ends. If not passed — the description stays empty, and it can be set later via PATCH /v1/infra/servers/:id |
plan |
string | yes | Plan ID from GET /v1/infra/providers/:providerId/plans, for example bc-small |
region |
string | yes | Region ID from GET /v1/infra/providers/:providerId/regions, for example bc-eu-central |
image |
string | no | OS image ID from GET /v1/infra/providers/:providerId/images. If omitted — the platform resolves the provider's current image itself. If you pass it, take the current one from the response: the ID changes when the build is updated |
sshPublicKey |
string | no | Your SSH public key (ssh-rsa …, ssh-ed25519 …, ecdsa-sha2-nistp256/384/521 …, security keys). Up to 8192 characters. If not passed — the platform will generate a private key and return it once in ssh.privateKey |
placement |
string | no | Placement model: auto (default) or dedicated. On a Bitrix24 account in Galaxy First mode (galaxies-only), the value dedicated creates a standalone virtual machine instead of a Galaxy application — "graduating" the application onto its own server. It passes the same checks as a normal server create: the serverCreation policy and the per-user server quota. With auto the behavior is unchanged (in Galaxy First mode a Galaxy application is created) |
graduateFrom |
string | no | The identifier of your Galaxy application (kind=GALAXY_APP) to delete after the dedicated server is created (graduation cleanup — so the old application does not linger in the galaxy). Owner-scoped: the same key, the same Bitrix24 account, kind=GALAXY_APP. A foreign or non-Galaxy identifier returns 404 and deletes nothing. Meaningful only together with placement: dedicated. For a dedicated server, the Idempotency-Key header is not compatible with this parameter — see Idempotency |
Note: for the dedicated-virtual-machine model the
runtimeparameter is not accepted inPOST /v1/infra/servers— passing it will return400 RUNTIME_PARAM_REMOVED. The runtime is installed at the deploy stage. See POST /:id/deploy. Exception — Galaxy mode: whensourceis passed,runtimeis required (see the "Galaxy application" section), and400 RUNTIME_PARAM_REMOVEDis not returned in that case.
Examples
curl — personal key
# name — lowercase Latin letters, digits and hyphens only, first character a letter (^[a-z][a-z0-9-]*$)
curl -X POST https://vibecode.bitrix24.com/v1/infra/servers \
-H "X-Api-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"provider": "bitrix-cloud",
"name": "my-crm-app",
"displayName": "My CRM bot",
"plan": "bc-small",
"region": "bc-eu-central",
"image": "ubuntu-2404-lts"
}'
curl — OAuth application
# name — lowercase Latin letters, digits and hyphens only, first character a letter (^[a-z][a-z0-9-]*$)
curl -X POST https://vibecode.bitrix24.com/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-crm-app",
"displayName": "My CRM bot",
"plan": "bc-small",
"region": "bc-eu-central",
"image": "ubuntu-2404-lts"
}'
JavaScript — personal key
// name — lowercase Latin letters, digits and hyphens only, first character a letter (^[a-z][a-z0-9-]*$)
const res = await fetch('https://vibecode.bitrix24.com/v1/infra/servers', {
method: 'POST',
headers: {
'X-Api-Key': 'YOUR_API_KEY',
'Content-Type': 'application/json',
},
body: JSON.stringify({
provider: 'bitrix-cloud',
name: 'my-crm-app',
displayName: 'My CRM bot',
plan: 'bc-small',
region: 'bc-eu-central',
image: 'ubuntu-2404-lts',
}),
})
const { data } = await res.json()
console.log('Server ID:', data.id, 'Subdomain:', data.subdomain)
// Save the SSH credentials just in case — they are returned only once
if (data.ssh.privateKey) {
await saveLocally(`${data.id}.key`, data.ssh.privateKey)
}
JavaScript — OAuth application
// name — lowercase Latin letters, digits and hyphens only, first character a letter (^[a-z][a-z0-9-]*$)
const res = await fetch('https://vibecode.bitrix24.com/v1/infra/servers', {
method: 'POST',
headers: {
'X-Api-Key': 'YOUR_APP_KEY',
'Authorization': 'Bearer USER_SESSION_TOKEN',
'Content-Type': 'application/json',
},
body: JSON.stringify({
provider: 'bitrix-cloud',
name: 'my-crm-app',
displayName: 'My CRM bot',
plan: 'bc-small',
region: 'bc-eu-central',
image: 'ubuntu-2404-lts',
}),
})
Response fields
| Field | Type | Description |
|---|---|---|
success |
boolean | Always true on success |
data.id |
string (UUID) | Unique server identifier |
data.status |
string | Always "provisioning" right after creation. Wait for "running" in 1–3 minutes |
data.provider |
string | Echo of the passed provider |
data.name |
string | Echo of the passed name — the system name of the server |
data.kind |
string | Server type, always: STANDALONE or GALAXY_APP |
data.galaxyId |
string | Only for the galaxy branch (the galaxy host id); absent from a standalone response |
data.displayName |
string | null | Human-readable name. If displayName was not passed at creation — it will equal name |
data.description |
string | null | Echo of the passed description. The key is present only in the response for a dedicated virtual machine. For a Galaxy application, where createdVia equals galaxy, the key is absent from the response even though the description is stored — read it via GET /v1/infra/servers/:id |
data.ip |
string | null | Public IP. null right after creation, filled in when the virtual machine starts |
data.ssh.user |
string | User for SSH: root for new servers |
data.ssh.port |
number | SSH port: 22 |
data.ssh.password |
string | null | One-time! The root password. Save it right away — it will not be returned later. Needed when switching to OPEN mode |
data.ssh.privateKey |
string | null | One-time! Private key in OpenSSH format (if sshPublicKey was not passed at creation). Save it right away |
data.plan |
string | Echo of the passed plan. Exception — a Galaxy application (createdVia: "galaxy"): the host's plan is returned, not the one requested |
data.region |
string | The region the server actually landed in. May differ from the requested one when a zone fallback triggers (see "Known specifics"). For a Galaxy application — the host's region |
data.image |
string | Echo of the passed image |
data.mode |
string | Always "BLACKHOLE" right after creation |
data.createdVia |
string | "api" for calls via the v1 API, "ui" for calls from the Vibecode dashboard |
data.subdomain |
string | Subdomain for the application, for example app-92fb1c34. Used in appUrl |
data.blackholeStatus |
string | State of the agent tunnel. Right after creation — "NONE", then it passes through WAITING and ends at CONNECTED |
data.accessPolicy |
string | Access policy for the application. By default "OWNER_ONLY" — only the key owner |
data.runtimeId |
string | null | Always null at creation. Filled in on deploy with runtime |
data.runtimeStatus |
string | null | Always null at creation. Updated during deploy |
data.appUrl |
string | null | HTTPS address of the application: https://{subdomain}.vibecode.bitrix24.com |
data.createdAt |
string (ISO 8601) | Creation timestamp |
Response example
A dedicated virtual machine:
{
"success": true,
"data": {
"id": "db008c84-91a5-4e15-b9d5-6c6aa2838448",
"status": "provisioning",
"provider": "bitrix-cloud",
"name": "docs-test-temp",
"kind": "STANDALONE",
"ip": null,
"ssh": {
"user": "root",
"port": 22,
"password": "0FPIIR9EfO2OAeSSMK6bKA",
"privateKey": "-----BEGIN OPENSSH PRIVATE KEY-----\nb3BlbnNzaC1rZXktdjEAAAAABG5vbmU…\n-----END OPENSSH PRIVATE KEY-----\n"
},
"plan": "bc-small",
"region": "bc-eu-central",
"image": "ubuntu-2404-lts",
"mode": "BLACKHOLE",
"createdVia": "api",
"subdomain": "app-92fb1c34",
"blackholeStatus": "NONE",
"accessPolicy": "OWNER_ONLY",
"runtimeId": null,
"runtimeStatus": null,
"appUrl": "https://app-92fb1c34.vibecode.bitrix24.com",
"createdAt": "2026-04-22T10:50:11.477Z"
}
}
A Galaxy application via scenario 1 — source was passed, the build runs in the background. There are no ssh or next fields:
{
"success": true,
"data": {
"id": "7c2b1f08-3a4d-4e91-9b6c-2f5e8a1d0c33",
"status": "provisioning",
"provider": "bitrix-cloud",
"name": "my-crm-app",
"kind": "GALAXY_APP",
"galaxyId": "<galaxy-host-id>",
"displayName": "My CRM bot",
"ip": null,
"ssh": null,
"plan": "bc-medium",
"region": "bc-eu-central",
"image": "ubuntu-2404-lts",
"mode": "BLACKHOLE",
"createdVia": "galaxy",
"subdomain": "app-7c2b1f08",
"blackholeStatus": "NONE",
"accessPolicy": "OWNER_ONLY",
"runtimeId": null,
"runtimeStatus": null,
"appUrl": "https://app-7c2b1f08.vibecode.bitrix24.com",
"createdAt": "2026-04-22T10:50:11.477Z"
}
}
A Galaxy application via scenario 2 — source was not passed, next and hint appear:
{
"success": true,
"data": {
"id": "7c2b1f08-3a4d-4e91-9b6c-2f5e8a1d0c33",
"status": "provisioning",
"provider": "bitrix-cloud",
"name": "my-crm-app",
"kind": "GALAXY_APP",
"galaxyId": "<galaxy-host-id>",
"displayName": "My CRM bot",
"ip": null,
"ssh": null,
"plan": "bc-medium",
"region": "bc-eu-central",
"image": "ubuntu-2404-lts",
"mode": "BLACKHOLE",
"createdVia": "galaxy",
"subdomain": "app-7c2b1f08",
"blackholeStatus": "NONE",
"accessPolicy": "OWNER_ONLY",
"runtimeId": null,
"runtimeStatus": null,
"appUrl": "https://app-7c2b1f08.vibecode.bitrix24.com",
"createdAt": "2026-04-22T10:50:11.477Z",
"next": "deploy",
"hint": "This is a galaxy app — POST /v1/infra/servers/:id/deploy with source.content now; it will not reach \"running\" on its own."
}
}
If a Galaxy application's build failed, GET /v1/infra/servers/:id returns the reason in data.provisionError:
{
"success": true,
"data": {
"id": "7c2b1f08-3a4d-4e91-9b6c-2f5e8a1d0c33",
"status": "error",
"createdVia": "galaxy",
"provisionError": "Docker build failed: npm ci exited with code 1"
}
}
Error response example
400 — validation failed (the name starts with an uppercase letter):
{
"success": false,
"error": {
"code": "INVALID_REQUEST",
"message": "name: Name must start with a letter and contain only lowercase letters, digits, and hyphens"
}
}
Errors
| HTTP | Code | Description |
|---|---|---|
| 400 | INVALID_REQUEST |
Field validation failed (invalid name format, a required field is missing, an invalid SSH key, etc.). The message field contains the specific reason. On a portal that places applications in a galaxy, when the body carries neither source nor the full provider + plan + region tuple, an error.hint object is added (see below) |
| 400 | SOURCE_AT_CREATE_GALAXY_ONLY |
The source field was passed on a portal without Galaxy mode. Upload the code via POST /:id/deploy after creation |
| 400 | RUNTIME_PARAM_REMOVED |
The runtime parameter was passed without source. Specify the runtime in POST /:id/deploy, or pass runtime, start and source together in this same request. On a portal that places applications in a galaxy, an error.hint object is added (see below) |
| 400 | UNKNOWN_PARAM |
The request body has an unknown field (e.g. deployMode instead of placement). details.unknownFields lists the extra fields, details.suggestions proposes the correct name, and details.validParams is the full list of accepted fields |
| 401 | MISSING_API_KEY |
The X-Api-Key header was not passed |
| 401 | INVALID_API_KEY |
Invalid or expired API key |
| 402 | COMMERCIAL_PLAN_REQUIRED |
The Bitrix24 plan is free and the trial period is unavailable (already used). See the Plan and access section |
| 402 | TRIAL_EXPIRED |
The trial was used and has ended |
| 402 | TRIAL_PORTAL_LIMIT |
The portal's server quota on trial was exceeded |
| 402 | TRIAL_USER_LIMIT |
The per-user server quota on trial was exceeded |
| 402 | PLAN_NOT_ALLOWED_ON_TRIAL |
The requested plan is not available during the trial period. error.details.allowedPlans lists the plans the trial gate is configured for; error.details.requestedPlan, when present, echoes the rejected plan |
| 402 | ACCOUNT_FROZEN |
The Vibecode balance is frozen, a top-up is required |
| 403 | INFRA_NOT_PERMITTED |
Infrastructure is disabled on the platform or on the portal |
| 403 | SERVER_CREATION_DISABLED |
Server creation is forbidden by portal policy |
| 403 | MAX_SERVERS_REACHED |
The per-API-key server limit was exceeded. Delete unneeded ones via DELETE or create a new API key |
| 403 | WRITE_BLOCKED_READONLY_KEY |
The key is in read-only mode, but creating a server is a write operation. Switch the key to read and write — see Access mode |
| 403 | INFRA_SCOPE_REQUIRED |
The key lacks the vibe:infra scope — infrastructure management is unavailable. Add the scope or use a key with infrastructure permissions |
| 403 | INFRA_FORBIDDEN_FOR_COWORK_KEY |
The call was made with a Co-work key. Such a key works with data only and does not manage servers. error.details.requiredAction carries the steps for obtaining a key with deploy rights |
| 404 | NO_CREDENTIALS |
The provider is not configured on the platform |
| 502 | PROVIDER_ERROR |
The cloud provider returned an error while creating the virtual machine. The server record is marked as deleted, the quota is not consumed — you can retry immediately. The message field contains the text from the provider |
| 429 | RATE_LIMITED |
The platform's overall request limit was exceeded |
| 503 | POOL_EXHAUSTED |
The service is temporarily overloaded — the database connection pool is exhausted. The response carries retryAfter and a Retry-After header, retry after a few seconds |
The full list of common API errors — Errors.
The `error.hint` object
On a Bitrix24 account that places applications in a galaxy, the INVALID_REQUEST and RUNTIME_PARAM_REMOVED errors are extended with an error.hint object. It names the reason for the refusal and gives a ready-made request body. The condition differs between the two codes. For INVALID_REQUEST the hint arrives when the body carries neither source nor the full provider + plan + region tuple. For RUNTIME_PARAM_REMOVED it arrives when runtime is passed without source, even if the tuple is filled in completely. On a Bitrix24 account with dedicated virtual machines there is no hint in either case.
Do not confuse it with data.hint from a successful response: that one is a string about the next step after creating an application via scenario 2, this one is an object inside error.
| Field | Type | Description |
|---|---|---|
error.hint.reason |
string | Why the request was refused |
error.hint.recovery |
string | What to change in the request so it succeeds |
error.hint.example |
object | A ready-made request body to start from |
{
"success": false,
"error": {
"code": "INVALID_REQUEST",
"message": "provider: Required; plan: Required; region: Required",
"hint": {
"reason": "This portal places new apps on shared galaxy hosts, and the request lacked `source` and the full provider/plan/region tuple.",
"recovery": "RECOMMENDED: create-and-deploy in ONE call — POST /v1/infra/servers { name, source: { content }, runtime, start }; OMIT provider/plan/region. Two-step also works: pass provider/plan/region (informational for a galaxy — values from GET /v1/infra/providers catalogs) to create an empty slot, then POST /v1/infra/servers/:id/deploy with the source. See GET /v1/me -> deployment.galaxyApp.checklist. For a deliberate dedicated standalone VM pass placement: \"dedicated\" together with provider/plan/region.",
"example": {
"name": "<slug>",
"source": { "content": "<base64 gzip-tar of the app>" },
"runtime": "node20",
"start": "node server.js",
"port": 3000
}
}
}
}
If the body carries placement: "dedicated", the hint is different: it suggests adding provider, plan and region while keeping the dedicated server, instead of moving the application into a galaxy container.
Idempotency
Pass the optional Idempotency-Key header to safely retry creating a standalone server. If the response to the first request is lost (a network drop, a load-balancer timeout), a retry with the same key does not create a second server — it returns the same server the first request created, with status 201 and an Idempotent-Replayed: true response header.
- The key is a 1–255 character string from
[A-Za-z0-9_.:-]. It is scoped to your API key. - On a replay the one-time SSH credentials are not re-exposed. In the response body
ssh.privateKeyandssh.passwordarenull, and anotefield explains this. Keep the credentials from the first create response. - The header applies to standalone servers only. On galaxy-placement Bitrix24 accounts a well-formed key is ignored without an error, and the retry protection does not extend to that path.
- The header is not supported together with
graduateFromfor a dedicated server — it returns400 IDEMPOTENCY_UNSUPPORTED_WITH_GRADUATION.
| Status | code |
When |
|---|---|---|
| 400 | INVALID_IDEMPOTENCY_KEY |
The key fails validation (length or disallowed characters) |
| 400 | IDEMPOTENCY_UNSUPPORTED_WITH_GRADUATION |
The key together with graduateFrom for a dedicated server |
| 409 | IDEMPOTENCY_KEY_ALREADY_USED |
The key was already used for a server that has since been deleted — pick a new key |
| 409 | IDEMPOTENCY_CONCURRENT_RETRY |
A concurrent request with the same key is still in progress — retry shortly |
Known specifics
- Examples of valid and invalid names by the pattern
^[a-z][a-z0-9-]*$:- Valid:
my-app,bot-1,crm-dashboard. - Invalid:
My-App(uppercase),bot_1(underscore),my.app(dot),café(non-ASCII letters).
- Valid:
sshPublicKeyvs auto-generation. If you pass your own public key — the platform does not generate a private one, andssh.privateKeyin the response will benull. The password inssh.passwordis still returned.- When
regionin the response differs from the requested one. The requested zone may have run out of IP addresses (Address space exhausted) — then the platform automatically tries the next zones in the order fromGET /v1/infra/providers/:providerId/regions, and records this in the audit log with theSERVER_ZONE_FALLBACKevent. - Provisioning timeout is 15 minutes. If
statusstays inprovisioninglonger, the platform moves the server toerrorand fills inprovisionErrorandprovisionErrorCode. After that, all that remains is to callDELETEand create a new one. - The runtime is installed at the deploy stage. The
runtimeparameter has been removed fromPOST /v1/infra/servers(it will return400 RUNTIME_PARAM_REMOVED). Specifyruntimein the body ofPOST /:id/deploy— it will be installed between archive extraction and application start.