## 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`](./deploy.md) and commands [`POST /exec`](./exec.md) — 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 |
|-----------|----|------|:--------:|-------------|
| `id` | path | string | yes | Server or Galaxy application ID from the [server list](/docs/infra/servers/list). |
| `limit` | query | integer | no | Number 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` |
| `kind` | query | string | no | Which kind of operation to return: `deploy` (the default), `exec`, or `all` for both. Any other value, an empty one included, returns `400 INVALID_KIND` |

## Examples

### curl — personal key

```bash
curl -H "X-Api-Key: YOUR_API_KEY" \
  'https://vibecode.bitrix24.com/v1/infra/servers/SERVER_ID/operations?limit=5'
```

### curl — OAuth application

```bash
curl -H "X-Api-Key: YOUR_APP_KEY" \
  -H "Authorization: Bearer USER_SESSION_TOKEN" \
  'https://vibecode.bitrix24.com/v1/infra/servers/SERVER_ID/operations?limit=5'
```

### JavaScript — personal key

```javascript
const response = await fetch("https://vibecode.bitrix24.com/v1/infra/servers/SERVER_ID/operations?limit=5", {
  headers: {
    "X-Api-Key": "YOUR_API_KEY",
  },
});
const body = await response.json();
```

### JavaScript — OAuth application

```javascript
const response = await fetch("https://vibecode.bitrix24.com/v1/infra/servers/SERVER_ID/operations?limit=5", {
  headers: {
    "X-Api-Key": "YOUR_APP_KEY",
    "Authorization": "Bearer USER_SESSION_TOKEN",
  },
});
const body = await response.json();
```

**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.

## Response fields

| Field | Type | Description |
|-------|------|-------------|
| `success` | boolean | Whether the request succeeded. |
| `data` | object | Result. |
| `data.operations` | array | Recent operations, newest first. |
| `data.operations[].operationId` | string | Identifier for [`GET /v1/infra/operations/:operationId`](./operation-status.md) |
| `data.operations[].kind` | string | Operation kind: `deploy` or `exec` |
| `data.operations[].serverId` | string | Server or Galaxy application ID from the [server list](/docs/infra/servers/list). |
| `data.operations[].outcomeAvailable` | boolean | Whether the operation outcome is still stored |
| `data.operations[].status` | string | `running`, `succeeded`, `failed`, or `unknown`; absent for an expired terminal outcome |
| `data.operations[].step` | string \| null | Last observed step. Always `null` on `kind: "exec"` rows — a command has no steps. Absent for an expired terminal outcome |
| `data.operations[].exitCode` | number \| null | `kind: "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 |
| `data.operations[].startedAt` | string | Start time, ISO 8601 |
| `data.operations[].finishedAt` | string \| null | Finish time; absent for an expired terminal outcome |
| `data.operations[].error` | object \| null | `{ code, message }` for `failed` only; absent for an expired terminal outcome |

## Response example

```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
      }
    ]
  }
}
```

## Known specifics

**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`](./operation-status.md).

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. To find 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`](/docs/infra/servers/get) and the [logs](./logs.md).
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`](/docs/infra/servers/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`](/docs/infra/servers/get)) and logs before BOTH steps: `/unstick`, whose tunnel bounce 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 `GALAXY_DEPLOY_INTERRUPTED` or `GALAXY_HOST_UNREACHABLE` responses returned after a connection drop, 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.


## Error response example

`400 INVALID_KIND` for `?kind=invalid`.

```json
{
  "success": false,
  "error": {
    "code": "INVALID_KIND",
    "message": "kind must be one of: deploy, exec, all"
  }
}
```

## Errors

| HTTP | Code | Description |
|------|------|-------------|
| 400 | `INVALID_LIMIT` | `limit` is negative, fractional, non-numeric, empty, or exceeds the safe-integer range |
| 400 | `INVALID_KIND` | `kind` was sent but is none of `deploy`, `exec`, `all`. An empty value counts as sent |
| 401 | `MISSING_API_KEY` | The `X-Api-Key` header was not provided |
| 401 | `INVALID_API_KEY` | The key is not recognized — no such key exists on the platform |
| 404 | `SERVER_NOT_FOUND` | The server is not available to this key |
| 429 | `RATE_LIMITED` | The 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](/docs/errors).

## See also

- [Outcome by identifier](./operation-status.md)
- [Deploy an application](./deploy.md)
- [Application logs](./logs.md)
- [Server activity feed](/docs/infra/servers/activity)
