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

Recent server operations

GET /v1/infra/servers/:id/operations

Returns recent operations on a server started by the current API key, newest first. There are two kinds of operation — deploys POST /deploy and commands POST /exec — and every row names its own kind in the kind field. By default the list returns deploys only: this endpoint predates commands, so without the parameter it returns the same set of rows as before. The rows themselves additionally carry a kind field. kind=exec shows commands and kind=all returns a mixed feed. This is the recovery channel when the response is lost before the client receives an operationId — notably when a connection drops during a Galaxy application build, or when the wait for a long command is interrupted.

The endpoint only reads the shared outcome record: it does not wake the server, repair the tunnel, or release the mutex. Server access matches deploy access, while rows are additionally filtered by the exact API key that started the operation. An application-linked key therefore sees only its own runs, and the shared Galaxy host ID does not expose its applications' runs. An owner can reconcile their operations after the server is soft-deleted.

Parameters

Parameter In Type Required Description
Parameterid Inpath Typestring Requiredyes DescriptionServer or Galaxy application ID
Parameterlimit Inquery Typeinteger Requiredno DescriptionNumber of rows: 5 by default, maximum 20. Zero is clamped to 1 and an integer above 20 to 20; a negative value, a fraction, a non-number, or an empty value returns 400 INVALID_LIMIT
Parameterkind Inquery Typestring Requiredno DescriptionWhich kind of operation to return: deploy (the default), exec, or all for both. Any other value, an empty one included, returns 400 INVALID_KIND

Example

Terminal
curl -H "X-Api-Key: YOUR_API_KEY" \
  'https://vibecode.bitrix24.com/v1/infra/servers/SERVER_ID/operations?limit=5'
JSON
{
  "success": true,
  "data": {
    "operations": [
      {
        "operationId": "clz9k2m4x0001qw8h3f7d2n5p",
        "kind": "deploy",
        "serverId": "8f14e45f-ceea-467a-9c8d-2f1c5a6b7e30",
        "status": "running",
        "outcomeAvailable": true,
        "step": "build",
        "startedAt": "2026-08-26T09:14:22.117Z",
        "finishedAt": null,
        "error": null
      }
    ]
  }
}

An empty parameter value is a value that was sent, not an omitted parameter. ?kind= and ?limit= come from a template with an unfilled variable — the caller meant to narrow the feed and failed. Such a call is rejected instead of silently defaulted: falling back would hand back a feed the caller never asked for. The kind=deploy and limit=5 defaults apply only when the key is absent from the query string altogether.

Operation fields

Field Type Description
FieldoperationId Typestring DescriptionIdentifier for GET /v1/infra/operations/:operationId
Fieldkind Typestring DescriptionOperation kind: deploy or exec
FieldserverId Typestring DescriptionServer or Galaxy application ID
FieldoutcomeAvailable Typeboolean DescriptionWhether the operation outcome is still stored
Fieldstatus Typestring Descriptionrunning, succeeded, failed, or unknown; absent for an expired terminal outcome
Fieldstep Typestring | null DescriptionLast observed step. Always null on kind: "exec" rows — a command has no steps. Absent for an expired terminal outcome
FieldexitCode Typenumber | null Descriptionkind: "exec" rows only. Command exit code if confirmed by the agent, otherwise null. On a deploy row the field is absent rather than null. Also absent for an expired terminal outcome
FieldstartedAt Typestring DescriptionStart time, ISO 8601
FieldfinishedAt Typestring | null DescriptionFinish time; absent for an expired terminal outcome
Fielderror Typeobject | null Description{ code, message } for failed only; absent for an expired terminal outcome

On kind: "exec" rows, judge by the pair exitCode + 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 did not report an exit code, so do not re-run it; with failed it means only that this attempt never started; before retrying, check the operation occupying the channel and the outcomes of earlier attempts whose acknowledgements were lost; with running it means the run is still in flight; and only with unknown is nothing proven. The statuses are covered in detail in GET /v1/infra/operations/:operationId.

A terminal outcome is retained for 7 days. The row then remains in the list for a while with outcomeAvailable: false, but its status, step, and error are no longer exposed. unknown is a stable terminal answer when the platform cannot prove the remote outcome, for example after GATEWAY_UNREACHABLE; an expired operation still marked running is also shown as unknown.

Safe recovery after a dropped connection

  1. Do not redeploy or re-run a state-changing command blindly. Find this key's recent operation in the list. Looking for a command — pass kind=exec: without the parameter the list shows deploys only.
  2. While it is running, wait and poll the list or the endpoint for that operation every few seconds.
  3. Use a confirmed succeeded outcome. A failed exec proves only that this attempt did not start: inspect its error before deciding whether to retry. For unknown or no matching record, reconcile GET /v1/infra/servers/:id and the logs.
  4. If a later request confirms EXEC_BUSY, look at error.hint.holder in that same 409 response: it names the operation holding the lock regardless of which key started it. A holder means an operation is running: wait, do not force-release. The operation list does not answer this question: it shows only the runs of your own key, so an operation started by another key of the same account is absent from it, and an empty list is no proof that the server is idle. The absence of a holder does not prove that the server is idle: the holder lookup is fail-open, so it also reports "no holder" when the lookup fails. Reconcile any earlier attempt with a lost acknowledgement, the server state, and the logs before retrying after Retry-After or using the guarded POST /v1/infra/servers/:id/unstick, which answers 409 against a live operation as long as no force is passed. Independently confirm that the server is idle by checking its state (GET /v1/infra/servers/:id) and logs before BOTH steps: /unstick, whose tunnel restart kills anything the agent is running, and especially the unconditional DELETE /lock that follows it. Then account for the server kind. For STANDALONE, you may call POST /v1/infra/servers/:id/unstick and repeat the read-only checks. A GALAXY_APP uses its shared host's exec channel: tenant unstick is unsupported; wait 30–60 seconds and contact support if EXEC_BUSY persists. After either recovery, reconcile the list, state, and logs again before redeploying.

Standalone deploy responses with a transport reason of GATEWAY_UNREACHABLE, DEPLOY_CONNECTION_TERMINATED, DEPLOY_TIMEOUT, or DEPLOY_TUNNEL_STALE, and Galaxy responses with GALAXY_DEPLOY_INTERRUPTED or GALAXY_HOST_UNREACHABLE after a connection drops carry this read-first sequence in error.hint. A Galaxy response in this state carries error.retryable: false: the dropped connection by itself proves neither success nor failure. The slot, container, and /data are preserved while the outcome is unknown — do not delete or recreate the app.

Errors

HTTP Code Description
HTTP400 CodeINVALID_LIMIT Descriptionlimit is negative, fractional, non-numeric, empty, or exceeds the safe-integer range
HTTP400 CodeINVALID_KIND Descriptionkind was sent but is none of deploy, exec, all. An empty value counts as sent
HTTP401 CodeMISSING_API_KEY DescriptionThe X-Api-Key header was not provided
HTTP401 CodeINVALID_API_KEY DescriptionThe key is not recognized — no such key exists on the platform
HTTP404 CodeSERVER_NOT_FOUND DescriptionThe server is not available to this key
HTTP429 CodeRATE_LIMITED DescriptionThe limit of 60 requests per minute per key was exceeded. The current limit is returned in the x-ratelimit-limit header

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

See also