## Operation outcome from the server feed

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

Returns the outcome of a deploy or command that an entry in the server activity feed refers to. The operation is looked up by server ID and operation ID.

Use it when the outcome is read by a key other than the one that started the operation: [`GET /v1/infra/operations/:operationId`](./operation-status.md) answers only the creator key, while this endpoint follows the access rule of the [activity feed](/docs/infra/servers/activity). It is read by the server owner with a personal key or with the key of their OAuth application from the same Bitrix24 account, and by a development team member whose role includes server settings, with their own personal key. An OAuth application key reads the feed with the rights of its owner: the session user does not affect access, and the session token can be omitted. Keys issued for a narrow task with their own route list — the agent maintenance key, the application external API key, the team member key and the external collaborator key — do not read the outcome at this path and get `403` with a code of the form `…_KEY_OUT_OF_SCOPE`. The response body is the same as for [`GET /v1/infra/operations/:operationId`](./operation-status.md). The endpoint is read-only and has no side effects.

## Parameters

| Parameter | Type | Required | Description |
|----------|-----|:-----:|----------|
| `id` (path) | string | yes | Server ID. List: [`GET /v1/infra/servers`](/docs/infra/servers/list) |
| `operationId` (path) | string | yes | Operation identifier — the `ref.id` field of an [activity feed](/docs/infra/servers/activity) entry, or the identifier from the [`POST /deploy`](./deploy.md) or [`POST /exec`](./exec.md) response |

## Examples

### curl — personal key

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

### 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/OPERATION_ID
```

### JavaScript — personal key

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

if (res.status === 410) {
  console.log('The operation existed, details are no longer stored')
} else {
  const { data } = await res.json()
  console.log(`Status: ${data.status}, step: ${data.step}, reason: ${data.error?.code ?? '—'}`)
}
```

### JavaScript — OAuth application

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

## Response fields

| Field | Type | Description |
|------|-----|----------|
| `success` | boolean | Always `true` on success |
| `data.operationId` | string | Operation identifier |
| `data.kind` | string | Operation kind: `deploy` or `exec` |
| `data.serverId` | string | Server ID |
| `data.status` | string | `running`, `succeeded`, `failed` or `unknown` |
| `data.step` | string \| null | The step the operation is at or stopped at. Always `null` for `kind: "exec"` |
| `data.exitCode` | number \| null | Only for `kind: "exec"`: the command exit code if the agent confirmed it, otherwise `null`. A deploy has no such field |
| `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`. `code` may be `null` if the platform did not name a failure code |

The meaning of the statuses and of the `exitCode` + `status` pair is on the [Operation outcome](./operation-status.md) page.

## Response example

```json
{
  "success": true,
  "data": {
    "operationId": "cmop1abcdefghijklmnopqrs",
    "kind": "deploy",
    "serverId": "8f14e45f-ceea-467a-9c8d-2f1c5a6b7e30",
    "status": "failed",
    "step": "download",
    "startedAt": "2026-10-06T12:13:17.762Z",
    "finishedAt": "2026-10-06T12:13:28.135Z",
    "error": {
      "code": "DOWNLOAD_HTTP_STATUS",
      "message": "The source returned HTTP 404."
    }
  }
}
```

## Error response example

404 — the server does not exist, is not available to this key, or the feed is not yet enabled for the server owner:

```json
{
  "success": false,
  "error": {
    "code": "SERVER_NOT_FOUND",
    "message": "Server not found"
  }
}
```

## Errors

| HTTP | Code | Description |
|------|-----|----------|
| 400 | `INVALID_OPERATION_ID` | The identifier is not of the shape the platform issues |
| 401 | `MISSING_API_KEY` | The `X-Api-Key` header is not provided |
| 401 | `INVALID_API_KEY` | The key is not recognized |
| 401 | `INVALID_SESSION` | An OAuth application key is passed with a session token that is not recognized or has expired |
| 403 | `SESSION_APP_MISMATCH` | The session token was issued to a different application or for a different Bitrix24 account than the OAuth application key in `X-Api-Key` |
| 403 | `AGENT_MAINTENANCE_KEY_OUT_OF_SCOPE` | The request is made with an agent maintenance key: it has its own route list |
| 403 | `APP_API_KEY_OUT_OF_SCOPE` | The request is made with an application external API key: it has its own route list |
| 403 | `EXTERNAL_COLLABORATOR_KEY_OUT_OF_SCOPE` | The request is made with an external collaborator key: it has its own route list |
| 403 | `PORTAL_COLLABORATOR_KEY_OUT_OF_SCOPE` | The request is made with a team member key. A team member reads the outcome with their personal key |
| 403 | `SERVER_ROLE_FORBIDDEN` | You are in the server development team, but the role does not include server settings |
| 404 | `SERVER_NOT_FOUND` | The server does not exist, is not available to this key, or the feed is not yet enabled for the server owner |
| 404 | `OPERATION_NOT_FOUND` | The server is available, but it has no such operation: the identifier is unknown, belongs to another server, or the record has already been deleted |
| 410 | `OPERATION_OUTCOME_EXPIRED` | The outcome is no longer stored — more than 7 days have passed since completion. The error body also carries the operation's `kind`, `serverId` and `startedAt` |
| 429 | `RATE_LIMITED` | The limit of 60 requests per minute per key is exceeded. The exact value is in the `x-ratelimit-limit` header, the ceiling is divided among replicas |

For the full list of common API errors, see [Errors](/docs/errors).

## Known specifics

- **Someone else's server and a disabled feed give the same `404 SERVER_NOT_FOUND`.** A separate response would confirm that such a server exists.
- **The outcome is readable even for an operation that is not in the feed.** The endpoint looks the operation up by server, not by feed entry, so it also answers for a deploy started before the feed was enabled for the server owner — while the outcome is stored.
- **`OPERATION_NOT_FOUND` is returned only to someone who can see the server.** An operation of another server is not read at this path, even if both servers are yours. A feed entry has no `serverId` of its own: take `ref.id` from the feed of the server whose `id` is in the request path.

## See also

- [Server activity feed](/docs/infra/servers/activity)
- [Operation outcome](./operation-status.md)
- [Recent server operations](./operations.md)
- [Service logs](./logs.md)
