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

Operation outcome

GET /v1/infra/operations/:operationId

Returns the outcome of one specific run by its operation id. The endpoint serves two kinds of operation — a deploy POST /deploy and a command POST /exec. Use it when the connection dropped and the terminal response never reached the client: the id tells you how the run ended instead of forcing a blind retry.

For a command this is the only way to learn the outcome after a drop. A transport timeout ends only the client-side wait. The command itself keeps running on the server and can finish after the client already received an error. That is why re-running a state-changing command may apply the change a second time.

The endpoint is read-only and has no side effects. Every run is addressable on its own, including earlier attempts on the same server. Outcomes are kept for 7 days.

Both deploy kinds receive an id, but at different points:

  • for a standalone virtual machine (kind: "STANDALONE"), the record is opened before the pipeline. If the machine is sleeping, the row is already visible as running in the server operation list while the blocking wake is in progress, but no HTTP status, header, or SSE frame reaches this connection until the wake completes. The header and first SSE frame arrive before the first deploy step;
  • for a Galaxy application (kind: "GALAXY_APP"), the record is opened after the application mutex is acquired, on entry to the first step. The id arrives only with the terminal JSON response, in the header and body. If the connection dropped earlier, find the run through the server operation list.

For a command POST /exec, the record is opened after the server lock is acquired and before dispatch to the agent. With durable async=true, only STANDALONE returns the id in the immediate 202 header and body after the bounded launch acknowledgement wait; the command continues independently. Synchronous standalone exec sends the header before waiting for completion. Synchronous Galaxy exec sends it only with the terminal response: an interrupted wait may receive no id, so find the run through the server operation list with ?kind=exec.

Where the id comes from — three channels, all carried by the same run:

  • the X-Vibe-Operation-Id response header — immediate for a command and for a deploy on an already available standalone machine, delayed until the blocking wake completes for a sleeping standalone deploy, and terminal-response-only for synchronous Galaxy exec and Galaxy deploy; durable async=true exec returns it with 202 on STANDALONE only;
  • the first event: operation frame with ?stream=true — for synchronous exec and for a deploy on a standalone machine, but only after any blocking wake; durable exec has no SSE; a browser EventSource does not expose response headers, so this is its only pre-pipeline channel;
  • the operationId field in the body — data.operationId on success, error.operationId on failure.

A missing id does not mean the deploy never started. A sleeping standalone connection may have dropped during the blocking wake, a Galaxy connection may have dropped before the terminal response, or the platform may have failed to open a record and continued without an id. Do not redeploy blindly: first call GET /v1/infra/servers/:id/operations, then reconcile the server state and logs.

Durable exec is standalone only. async=true accepts a timeout from 1 to 86400 seconds on STANDALONE. Other server kinds receive 400 DURABLE_EXEC_UNSUPPORTED_SERVER_KIND before locking, operation creation or command dispatch, without a synchronous fallback. Galaxy exec stays synchronous with a 600-second limit. A running standalone durable job keeps its execution channel busy after 202; poll this operation rather than resend the command. An unknown outcome needs reconciliation and confirmed termination. /unstick and lock deletion do not clear durable admission.

Parameters

Parameter In Type Required Description
ParameteroperationId Inpath Typestring Requiredyes DescriptionOperation ID from the POST /deploy or POST /exec response

Examples

curl — personal key

Terminal
curl -H "X-Api-Key: YOUR_API_KEY" \
  https://vibecode.bitrix24.com/v1/infra/operations/OPERATION_ID

curl — OAuth application

Terminal
curl -H "X-Api-Key: YOUR_APP_KEY" \
  -H "Authorization: Bearer USER_SESSION_TOKEN" \
  https://vibecode.bitrix24.com/v1/infra/operations/OPERATION_ID

JavaScript — personal key

javascript
// Deploy: after any blocking wake, take the id from the header BEFORE the body arrives.
// If the connection drops during wake, discover the id through the server operation list.
const deploy = await fetch(
  `https://vibecode.bitrix24.com/v1/infra/servers/${serverId}/deploy`,
  {
    method: 'POST',
    headers: { 'X-Api-Key': 'YOUR_API_KEY', 'Content-Type': 'application/json' },
    body: JSON.stringify({ source: { content: archiveBase64 }, start: 'node index.js' }),
  }
)
const operationId = deploy.headers.get('X-Vibe-Operation-Id')

// The connection dropped — ask for the outcome instead of redeploying
const res = await fetch(
  `https://vibecode.bitrix24.com/v1/infra/operations/${operationId}`,
  { headers: { 'X-Api-Key': 'YOUR_API_KEY' } }
)

if (res.status === 410) {
  console.log('The operation existed. Its outcome is no longer stored.')
} else if (res.status === 404) {
  console.log('No such operation')
} else {
  const { data } = await res.json()
  console.log(`Status: ${data.status}, step: ${data.step}`)
}

JavaScript — OAuth application

javascript
const res = await fetch(
  `https://vibecode.bitrix24.com/v1/infra/operations/${operationId}`,
  {
    headers: {
      'X-Api-Key': 'YOUR_APP_KEY',
      'Authorization': 'Bearer USER_SESSION_TOKEN',
    },
  }
)

Response fields

Field Type Description
Fieldsuccess Typeboolean DescriptionAlways true on success
Fielddata.operationId Typestring DescriptionOperation ID
Fielddata.kind Typestring DescriptionOperation kind: deploy or exec
Fielddata.serverId Typestring DescriptionID of the server the operation targets
Fielddata.status Typestring Descriptionrunning, succeeded, failed or unknown — see the table below
Fielddata.step Typestring | null DescriptionPipeline step the operation is on, or stopped at. Always null for kind: "exec" — a command has no steps
Fielddata.exitCode Typenumber | null Descriptionkind: "exec" only. Exit code of the command if confirmed by the agent, otherwise null. A number arrives only together with status: "succeeded" — see the pair rule below the status table
Fielddata.stdout Typestring (optional) DescriptionRetained durable exec standard output, including partial output for unknown when available; only for a scoped key owned by the operation creator
Fielddata.stderr Typestring (optional) DescriptionRetained durable exec error stream; same private scope as stdout
Fielddata.duration Typenumber (optional) DescriptionRetained durable exec duration in milliseconds; same private scope as stdout
Fielddata.truncated Typeboolean (optional) DescriptionWhether retained output reached the shared 1 MiB stdout+stderr limit; same private scope as stdout
Fielddata.startedAt Typestring DescriptionOperation record creation time, ISO 8601. For durable exec it does not prove the process launched
Fielddata.finishedAt Typestring | null DescriptionFinish time, ISO 8601. null while the operation is running
Fielddata.error Typeobject | null Description{ code, message } when status: "failed", otherwise null

Values of data.status:

Value Meaning
Valuerunning MeaningNo terminal operation outcome has been recorded yet. A durable command may be executing or awaiting launch confirmation, including after a lost ACK; this status does not prove a process launched. Poll the same operation instead of resending the command
Valuesucceeded MeaningFor a deploy it means it finished successfully. For a command it means it ran to completion, and its own verdict is in data.exitCode — a non-zero exit status arrives with this status too, because the status answers "did it run", not "what was the result"
Valuefailed MeaningThe reason is in data.error. For a deploy it means the deploy failed. For a command it means the command never ran: the status is set only when the platform can prove that — for example on EXEC_BUSY, where the agent was busy with another command. This proves only the refused attempt never ran; for EXEC_BUSY, check the operation occupying the channel and any earlier attempts with lost acknowledgements before retrying
Valueunknown MeaningThe platform cannot prove the remote outcome: the transport was lost or the process died between the start and the outcome write. The operation record exists, but it proves neither that the command started nor that it stopped. For a command it means the command may be executing on the server right now, so reconcile the server state in GET /v1/infra/servers/:id and the logs before re-running a state-changing command

Judge by the pair data.exitCode + data.status, never by one field alone. A number arrives only with succeeded and means the command ran to completion. null on its own does NOT mean "the outcome is not proven": with succeeded it means the command ran but the agent named no code, so do not re-run it; with failed it means this attempt never started; EXEC_BUSY still requires reconciliation of the occupying operation before retrying; with running the attempt awaits completion or launch confirmation, so poll the same operation; with unknown the outcome is not proven.

What is stored and what is not. A synchronous exec record carries the outcome of the attempt — status, exit status and the reason for a refusal — without stdout/stderr. Use async=true on STANDALONE for a durable command with encrypted retained output. This endpoint returns that output only when the current scoped key belongs to the user who created the operation. A same-owner key rotation preserves access; transfer to a different owner moves operation metadata but never its private output. Rows without a recorded creator identity return metadata only. Server-scoped reads never return command output.

Durable exec refusal codes

For kind: "exec" and status: "failed", the command was proven not to have started. data.error.code can name an admission refusal already returned by POST, or a prelaunch refusal discovered after HTTP 202. The HTTP status of this GET remains 200; these codes describe the recorded command attempt, not a failed status read. Confirm the operation status before starting a corrected request. For EXEC_BUSY, read the server operation list with kind=exec and reconcile the occupying attempt, including lost acknowledgements. Wait for a known running operation; an unknown outcome requires state and log checks or support, rather than another exec.

Code Condition Client action
CodeEXEC_JOB_STORAGE_UNAVAILABLE ConditionThe operation or its target could not be stored Client actionCheck the operation if an id was returned; resolve storage availability before retrying
CodeEXEC_BUSY ConditionExecution admission was occupied Client actionWait for the known running job; contact support for an unknown outcome
CodeEXEC_JOB_CAPACITY ConditionThe host already retains 64 job records Client actionWait for eligible retention cleanup or contact support; no automatic resend
CodeEXEC_JOB_UNAVAILABLE ConditionDurable admission, storage or launch is unavailable Client actionResolve availability with support; do not replace with a synchronous execution
CodeEXEC_JOB_INVALID ConditionThe agent rejected launch data or the launch window Client actionCheck the recorded refusal and request; contact support if a valid request is refused
CodeEXEC_JOB_REJECTED ConditionAdmission was refused without a specific code Client actionContact support
CodeUNKNOWN_TYPE ConditionThe execution service does not support durable jobs Client actionContact support to update execution support; no synchronous fallback
CodeEXEC_JOB_START_EXPIRED ConditionThe launch window expired before the user command started Client actionA new launch is safe after confirming this failed status; contact support if it repeats
CodeEXEC_WORKDIR_DENIED ConditionThe working directory violates the path policy Client actionChoose an allowed directory; see the exec workdir contract
CodeEXEC_WORKDIR_NOT_FOUND ConditionThe working directory is absent or cannot be inspected Client actionUse an existing accessible directory
CodeEXEC_WORKDIR_NOT_DIR ConditionThe working path is not a directory Client actionUse a directory
CodeEXEC_FAILED ConditionThe standalone command interpreter could not start Client actionCheck the environment and contact support before a new launch

An expired deadline or a lost standalone process can yield status: "unknown", partial retained output and data.error: null. This is not a prelaunch refusal and does not authorize another exec. Reconcile state and logs, confirm execution has stopped and contact support if needed. TTL, tunnel bounce, /unstick, force and lock deletion do not clear durable admission.

Response example

Deploy in progress:

JSON
{
  "success": true,
  "data": {
    "operationId": "clz9k2m4x0001qw8h3f7d2n5p",
    "kind": "deploy",
    "serverId": "8f14e45f-ceea-467a-9c8d-2f1c5a6b7e30",
    "status": "running",
    "step": "install",
    "startedAt": "2026-08-11T09:14:22.117Z",
    "finishedAt": null,
    "error": null
  }
}

Deploy finished successfully:

JSON
{
  "success": true,
  "data": {
    "operationId": "clz9k2m4x0001qw8h3f7d2n5p",
    "kind": "deploy",
    "serverId": "8f14e45f-ceea-467a-9c8d-2f1c5a6b7e30",
    "status": "succeeded",
    "step": null,
    "startedAt": "2026-08-11T09:14:22.117Z",
    "finishedAt": "2026-08-11T09:18:04.902Z",
    "error": null
  }
}

Deploy failed:

JSON
{
  "success": true,
  "data": {
    "operationId": "clz9k2m4x0001qw8h3f7d2n5p",
    "kind": "deploy",
    "serverId": "8f14e45f-ceea-467a-9c8d-2f1c5a6b7e30",
    "status": "failed",
    "step": "healthcheck",
    "startedAt": "2026-08-11T09:14:22.117Z",
    "finishedAt": "2026-08-11T09:17:41.338Z",
    "error": {
      "code": "DEPLOY_STEP_FAILED",
      "message": "connect ECONNREFUSED 127.0.0.1:3000"
    }
  }
}

A durable command ran to completion; retained fields are shown to its creator through the scoped key:

JSON
{
  "success": true,
  "data": {
    "operationId": "clz9k2m4x0001qw8h3f7d2n5q",
    "kind": "exec",
    "serverId": "8f14e45f-ceea-467a-9c8d-2f1c5a6b7e30",
    "status": "succeeded",
    "step": null,
    "exitCode": 0,
    "stdout": "maintenance complete\n",
    "stderr": "",
    "duration": 275000,
    "truncated": false,
    "startedAt": "2026-08-11T09:20:02.004Z",
    "finishedAt": "2026-08-11T09:24:37.815Z",
    "error": null
  }
}

The wait for the command was interrupted, the outcome is unknown:

JSON
{
  "success": true,
  "data": {
    "operationId": "clz9k2m4x0001qw8h3f7d2n5r",
    "kind": "exec",
    "serverId": "8f14e45f-ceea-467a-9c8d-2f1c5a6b7e30",
    "status": "unknown",
    "step": null,
    "exitCode": null,
    "startedAt": "2026-08-11T09:31:10.220Z",
    "finishedAt": "2026-08-11T09:36:41.905Z",
    "error": null
  }
}

Error response example

410 — the operation existed, but its outcome is no longer stored:

JSON
{
  "success": false,
  "error": {
    "code": "OPERATION_OUTCOME_EXPIRED",
    "message": "The outcome of this deploy is no longer stored (kept for 7 days).",
    "serverId": "8f14e45f-ceea-467a-9c8d-2f1c5a6b7e30",
    "startedAt": "2026-07-30T11:02:19.400Z"
  }
}

Errors

HTTP Code Description
HTTP400 CodeINVALID_OPERATION_ID DescriptionThe ID is not in the format the platform hands out
HTTP401 CodeMISSING_API_KEY DescriptionThe X-Api-Key header is missing
HTTP401 CodeINVALID_API_KEY DescriptionThe key is not recognized — no such key exists on the platform
HTTP404 CodeOPERATION_NOT_FOUND DescriptionNo such operation, it belongs to a different key, or the record has been removed
HTTP410 CodeOPERATION_OUTCOME_EXPIRED DescriptionThe operation is yours, but its outcome is no longer stored
HTTP429 CodeRATE_LIMITED DescriptionPolling limit exceeded

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

Known specifics

  • Someone else's operation answers exactly like a non-existent one, and that is deliberate. All three cases (no such id, an id belonging to another key, a removed record) return the same 404. A distinct answer for someone else's operation would confirm that the id exists, and the id is itself the access key to the record: guessing one would let you read someone else's deploy.
  • 410 differs from 404 only for the owner. "The outcome is no longer stored" means an operation record belonged to your key and its outcome window expired, seven days after its terminal outcome. It does not prove that the command or deploy ran: a refused attempt can also have an operation record. An id that belongs to someone else, or a made-up one, never receives that answer.
  • The record outlives the outcome. After 7 days the outcome stops being served, but the fact that the operation existed is kept for a while longer — that is what makes the distinguishable 410 possible at all. The record is removed afterwards, and the same id starts answering 404.
  • unknown is an answer, not an error. The operation record exists, but a transport or process loss means the platform cannot prove whether the command started or how it ended. Reconcile the current server state and logs first; redeploy only after that check.
  • The polling limit is 60 requests per minute per key. The current limit is returned in the x-ratelimit-limit header. An outcome does not change faster than the deploy itself, so polling every few seconds is enough.
  • Read through the key currently assigned to the operation. Key rotation or transfer moves operation metadata to the replacement key. Retained command output is available only when that key belongs to the user who created the operation: a same-owner rotation preserves output access, while a transfer to another owner returns metadata only. The server owner under a key outside the operation scope, and a development team member with the settings permission, read metadata through GET /v1/infra/servers/:id/operations/:operationId.

See also