# Computers

> Watch or take over a computer's desktop, and see who had control.

A computer is an app with one machine that runs the computer image, a home volume mounted at `/data`, and a secret `COMPUTER_VIEW_KEY` that the viewer routes below use. The console and `ry computer create` build one in that order: the app (with `"computer": true`, so the organization's computer limit is judged first), the home volume, the key, the deploy. There is no separate computer object in the API: read it through [Apps](https://docs.rytiv.com/reference/api/apps.md) and [Machines](https://docs.rytiv.com/reference/api/machines.md), and run commands in it with `ry exec` or the MCP tool `computer_exec`.

See the guide [Computers](https://docs.rytiv.com/computers.md) for sizes, idle shutdown and how to connect a coding agent.

## Endpoints

- [`POST /v1/machines/:id/viewer`](#post-v1machinesidviewer): A link to watch the computer's desktop (`mode: "view"`), or to take keyboard and mouse (`mode: "control"`). One control holder per machine
- [`POST /v1/machines/:id/control/release`](#post-v1machinesidcontrolrelease): Ends the control lease
- [`GET /v1/machines/:id/control-audit`](#get-v1machinesidcontrol-audit): Who got which link on this machine, from when to when, and how control ended
- [`GET /v1/apps/:name/control-audit`](#get-v1appsnamecontrol-audit): The same audit across all machines the app had

## POST /v1/machines/:id/viewer

A link to watch the computer's desktop (`mode: "view"`), or to take keyboard and mouse (`mode: "control"`). One control holder per machine.

**Auth:** API key with full access (a key with scopes cannot call this route). An agent token of class `sandbox` or `twin` may call it; class `production` is refused.

### Path parameters

| Name | Description |
| --- | --- |
| `id` | The object's id. |

### Request body

JSON, the shape `ViewerRequest` of the contract.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `mode` | `view` \| `control` | required | `view` to watch, `control` to take keyboard and mouse. |
| `ttlS` | integer | optional | How long the link opens a connection, in seconds. 60 to 3600, default `900`. |
| `who` | string | optional | Who the link is for; shown in the audit. 1 to 64 characters. |
| `force` | boolean | optional | Take control from the current holder; their connection ends. default `false`. |

### Example request

```bash
curl -X POST "https://api.rytiv.app/v1/machines/<id>/viewer" \
  -H "Authorization: Bearer $RYTIV_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"mode":"view"}'
```

### Response

A JSON body on success.

### Errors

| Status | Code | Meaning |
| --- | --- | --- |
| 400 | `bad-request` | The request is malformed or a field is out of range. `error` names every field; `issues` lists them. |
| 401 | `unauthenticated` | No API key, or one that does not work. |
| 403 | `token-scope` | The key's scopes do not cover this route. |
| 404 | `not-found` | Not found, or not yours. An object of another organization answers like one that does not exist. |
| 409 | `computer-control-missing` | The machine runs a computer image from before control leases; deploy it again. |
| 409 | `control-busy` | Another change of control is in progress; try again after `Retry-After`. |
| 409 | `control-held` | Somebody else holds control; the body names `heldBy`, `until` and `leaseId`. Use `force` to take it. |
| 409 | `guest-agent-absent` | The machine has no guest channel; it was created before shell sessions existed. Deploy it again. |
| 409 | `machine-stopped` | The machine is stopped; start it first. |
| 409 | `managed-service` | A managed database; its machine is not opened to a shell. |
| 409 | `not-a-computer` | The machine is not a computer (it has no `COMPUTER_VIEW_KEY`), or it is a console or job machine. |
| 409 | `several-machines` | Control needs exactly one machine; the app has several. |
| 409 | `view-key-too-short` | The computer's key is too short; the image would refuse to start with it. |
| 429 | `rate-limited` | Too many requests; wait for `Retry-After` and try again. |
| 502 | `guest-failed` | The control tool in the machine ran and failed. |
| 503 | `guest-channel-off` | The guest channel is switched off on the machine's host. |
| 503 | `guest-unreachable` | The guest did not answer; try again after `Retry-After`. |
| 503 | `not-placed` | The machine has no host yet. |
| 503 | `resource-check-failed` | Could not tell whether the machine backs a managed database; try again. |
| 503 | `view-key-unreadable` | The computer's key cannot be decrypted right now. |

## POST /v1/machines/:id/control/release

Ends the control lease.

**Auth:** API key with full access (a key with scopes cannot call this route). An agent token of class `sandbox` or `twin` may call it; class `production` is refused (`agent-token-production-readonly`).

### Path parameters

| Name | Description |
| --- | --- |
| `id` | The object's id. |

### Example request

```bash
curl -X POST "https://api.rytiv.app/v1/machines/<id>/control/release" \
  -H "Authorization: Bearer $RYTIV_TOKEN"
```

### Response

A JSON body on success.

### Errors

| Status | Code | Meaning |
| --- | --- | --- |
| 401 | `unauthenticated` | No API key, or one that does not work. |
| 403 | `token-scope` | The key's scopes do not cover this route. |
| 404 | `not-found` | Not found, or not yours. An object of another organization answers like one that does not exist. |
| 409 | `machine-stopped` | The machine is stopped; start it first. |
| 409 | `not-a-computer` | The machine is not a computer (it has no `COMPUTER_VIEW_KEY`), or it is a console or job machine. |
| 502 | `guest-failed` | The control tool in the machine ran and failed. |
| 503 | `guest-unreachable` | The guest did not answer; try again after `Retry-After`. |

## GET /v1/machines/:id/control-audit

Who got which link on this machine, from when to when, and how control ended.

**Auth:** API key with full access or the scope `read`. Every class of agent token may call it.

### Path parameters

| Name | Description |
| --- | --- |
| `id` | The object's id. |

### Example request

```bash
curl -X GET "https://api.rytiv.app/v1/machines/<id>/control-audit" \
  -H "Authorization: Bearer $RYTIV_TOKEN"
```

### Response

`200` with a JSON body.

### Errors

| Status | Code | Meaning |
| --- | --- | --- |
| 400 | `bad-request` | The request is malformed or a field is out of range. `error` names every field; `issues` lists them. |
| 401 | `unauthenticated` | No API key, or one that does not work. |
| 403 | `token-scope` | The key's scopes do not cover this route. |
| 404 | `not-found` | Not found, or not yours. An object of another organization answers like one that does not exist. |

## GET /v1/apps/:name/control-audit

The same audit across all machines the app had.

**Auth:** API key with full access or the scope `read`. Every class of agent token may call it.

### Path parameters

| Name | Description |
| --- | --- |
| `name` | The app's name. |

### Example request

```bash
curl -X GET "https://api.rytiv.app/v1/apps/<name>/control-audit" \
  -H "Authorization: Bearer $RYTIV_TOKEN"
```

### Response

`200` with a JSON body.

### Errors

| Status | Code | Meaning |
| --- | --- | --- |
| 400 | `bad-request` | The request is malformed or a field is out of range. `error` names every field; `issues` lists them. |
| 401 | `unauthenticated` | No API key, or one that does not work. |
| 403 | `token-scope` | The key's scopes do not cover this route. |
| 404 | `not-found` | Not found, or not yours. An object of another organization answers like one that does not exist. |
