For AI agents: markdown of this page — /docs-content-en/infra/deploy/operation-status.md documentation index — /llms.txt
Deploy outcome
GET /v1/infra/operations/:operationId
Returns the outcome of one specific deploy by its operation id. Use it when the connection to POST /deploy dropped and the terminal response never reached the client: the id tells you how the run ended instead of forcing a blind redeploy.
The endpoint is read-only and has no side effects. Every deploy 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, the header arrives immediately, and SSE also carries the id in its first frame; - 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.
Where the id comes from — three channels, all carried by the same deploy:
- the
X-Vibe-Operation-Idresponse header — immediate on a standalone machine, terminal-response-only for a Galaxy application; - the first
event: operationframe with?stream=true— standalone only; a browserEventSourcedoes not expose response headers, so this is its only early channel; - the
operationIdfield in the body —data.operationIdon success,error.operationIdon failure.
A missing id does not mean the deploy never started. For a Galaxy application, the connection may have dropped before the terminal response; the platform may also have failed to open a record, in which case the deploy continues without an id. Do not redeploy blindly: first call GET /v1/infra/servers/:id/operations, then reconcile the server state and logs.
Parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
operationId |
path | string | yes | Operation ID from the POST /deploy response |
Examples
curl — personal key
curl -H "X-Api-Key: YOUR_API_KEY" \
https://vibecode.bitrix24.com/v1/infra/operations/OPERATION_ID
curl — OAuth application
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
// Deploy: take the id from the header BEFORE the body arrives
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
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 |
|---|---|---|
success |
boolean | Always true on success |
data.operationId |
string | Operation ID |
data.kind |
string | Operation kind. Currently always deploy |
data.serverId |
string | ID of the server the deploy ran on |
data.status |
string | running, succeeded, failed or unknown — see the table below |
data.step |
string | null | Pipeline step the operation is on, or stopped at |
data.startedAt |
string | Start time, ISO 8601 |
data.finishedAt |
string | null | Finish time, ISO 8601. null while the operation is running |
data.error |
object | null | { code, message } when status: "failed", otherwise null |
Values of data.status:
| Value | Meaning |
|---|---|
running |
The deploy is in progress right now |
succeeded |
The deploy finished successfully |
failed |
The deploy failed, the reason is in data.error |
unknown |
The platform cannot prove the remote outcome: the transport was lost or the process died between the start and the outcome write. Not the same as "there was no deploy": the operation definitely ran. Check the current server state in GET /v1/infra/servers/:id |
Response example
Deploy in progress:
{
"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:
{
"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:
{
"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"
}
}
}
Error response example
410 — the operation existed, but its outcome is no longer stored:
{
"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 |
|---|---|---|
| 400 | INVALID_OPERATION_ID |
The ID is not in the format the platform hands out |
| 401 | MISSING_API_KEY |
The X-Api-Key header is missing |
| 401 | INVALID_API_KEY |
Invalid or expired API key |
| 404 | OPERATION_NOT_FOUND |
No such operation, it belongs to a different key, or the record has been removed |
| 410 | OPERATION_OUTCOME_EXPIRED |
The operation is yours, but its outcome is no longer stored |
| 429 | RATE_LIMITED |
Polling limit exceeded |
The full list of common API errors — 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" appears when the operation is yours and definitely ran, more than 7 days ago. 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
410possible at all. The record is removed afterwards, and the same id starts answering404. unknownis an answer, not an error. The operation definitely ran, but a transport or process loss means the platform does not know 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. An outcome does not change faster than the deploy itself, so polling every few seconds is enough.