# REST API basics

> The base URL, how requests are authenticated, the one error format every route answers with, and how to act for a managed organization.

Everything Rytiv does goes through one HTTP API. The CLI, the console and the MCP server use the same routes you can call yourself. The routes themselves are on the [API reference](https://docs.rytiv.com/reference/api.md) pages, one per family, generated from the control plane's code.

## Base URL

```text
https://api.rytiv.app/v1
```

Requests and answers are JSON (`Content-Type: application/json`), except where a route says otherwise: file contents, source uploads and the OAuth token endpoint take other bodies, and the reference says so.

## Authentication

Send an [API key](https://docs.rytiv.com/authentication.md) as a bearer token on every request:

**curl**

```bash
curl https://api.rytiv.app/v1/apps -H "Authorization: Bearer $RYTIV_TOKEN"
```

**TypeScript**

```ts
const res = await fetch("https://api.rytiv.app/v1/apps", {
  headers: { Authorization: `Bearer ${process.env.RYTIV_TOKEN}` },
});
const apps = await res.json();
```

**Python**

```python
import os, requests

res = requests.get(
    "https://api.rytiv.app/v1/apps",
    headers={"Authorization": f"Bearer {os.environ['RYTIV_TOKEN']}"},
)
apps = res.json()
```

**CLI**

```bash
ry apps list
```

Every route acts as the organization of the key. A name that belongs to another organization answers exactly like a name that does not exist, usually `404`. A key with [scopes](https://docs.rytiv.com/guides/agents-and-tokens.md) reaches only the routes its scopes cover; the reference names the scope each route needs.

### Acting for a managed organization

If your organization manages other organizations, a key of yours can act inside one of them with the header `X-Rytiv-Org: <org id>`. Naming your own organization changes nothing. Naming one your organization does not manage answers `403` with the code `delegation-refused`. An agent token cannot use the header at all (`agent-token-delegation`).

## Errors

Every error is a JSON object with an English sentence and a stable code:

```json
{ "error": "name already taken", "code": "name-taken" }
```

Branch on `code`, show `error`. Some routes add fields that help with the next step (`issues`, `limit`, `current`, `heldBy`). New codes can appear; a code your program does not know should fall back on the HTTP status. The [error catalog](https://docs.rytiv.com/concepts/errors.md) lists every code.

A request that fails validation answers `400` with the code `bad-request`. Its `error` sentence names every field that was wrong, and `issues` carries the same problems as a list.

| Status | Meaning |
| --- | --- |
| `400` | The request is malformed or a field is out of range. |
| `401` | No API key, or one that does not work. |
| `402` | The organization's credit is at zero; only reads, stops and billing routes pass. |
| `403` | The key may not do this: a scope, an agent token's class, an organization limit, or an organization yours does not manage. |
| `404` | Not found, or not yours. |
| `408` | A wait ran out (`GET /v1/machines/:id/wait`). |
| `409` | The request conflicts with the current state, for example a taken name or a deploy that is already running. |
| `429` | Too many: a limit of your organization is reached, or a rate limit. `Retry-After` says when to try again. |
| `5xx` | Something on our side, or no capacity right now. The `error` sentence says which. |

## Waiting for work to finish

A deploy, a machine create with `?wait=false`, a branch or a fork answer `202` and continue in the background. Poll the object they name until it reaches its final state:

| Started by | Poll | Until |
| --- | --- | --- |
| `POST /v1/apps/:name/deploy` | `GET /v1/deploys/:id` | `status` is `done` or `error` |
| `POST /v1/apps/:name/machines?wait=false` | `GET /v1/machines/:id/wait?state=running` | the machine answers, or `408` |
| `POST /v1/resources/:name/branch` or `/fork` | `GET /v1/resources/:name` | `state` is `ready` or `failed` |
| `POST /v1/apps/:name/jobs` | `GET /v1/jobs/:id` | `state` is `done`, `failed` or `timeout` |

## Rate limits

The product API has no general rate limit. A few entrances do, and answer `429` with `Retry-After` when it is reached: the MCP endpoint (120 tool calls per minute per organization), shell sessions (120 starts per minute per organization), computer viewer links (120 per minute per organization), and the login and OAuth entrances per caller address.

## Routes by family

| Family | Routes | Reference |
| --- | --- | --- |
| Apps | `/v1/apps`, attach, link, labels, maintenance | [Apps](https://docs.rytiv.com/reference/api/apps.md) |
| Deploys and releases | detect, builds, deploy, deploys, releases, rollback, previews, Git | [Deploys and releases](https://docs.rytiv.com/reference/api/deploys.md) |
| Machines | `/v1/apps/:name/machines`, `/v1/machines/:id`, logs, snapshots, files | [Machines](https://docs.rytiv.com/reference/api/machines.md) |
| Computers | viewer and control links, control audit | [Computers](https://docs.rytiv.com/reference/api/computers.md) |
| Sandboxes | `/v1/sandboxes` | [Sandboxes](https://docs.rytiv.com/reference/api/sandboxes.md) |
| Postgres | `/v1/resources` | [Postgres](https://docs.rytiv.com/reference/api/postgres.md) |
| Volumes | `/v1/volumes` | [Volumes](https://docs.rytiv.com/reference/api/volumes.md) |
| Buckets | `/v1/buckets`, presign, objects, CORS | [Buckets](https://docs.rytiv.com/reference/api/buckets.md) |
| Secrets | `/v1/apps/:name/secrets` | [Secrets](https://docs.rytiv.com/reference/api/secrets.md) |
| Tokens and keys | `/v1/tokens`, agent actions, SSH keys, device login | [Tokens and keys](https://docs.rytiv.com/reference/api/tokens.md) |
| Organization | `/v1/org`, managed organizations | [Organization](https://docs.rytiv.com/reference/api/organization.md) |
| Billing and usage | `/v1/billing/*`, `/v1/usage` | [Billing and usage](https://docs.rytiv.com/reference/api/billing.md) |
| Custom domains | `/v1/apps/:name/domains`, zones | [Custom domains](https://docs.rytiv.com/reference/api/domains.md) |
| Cron and jobs | schedules, jobs | [Cron and jobs](https://docs.rytiv.com/reference/api/cron-and-jobs.md) |
| Protection and network | protection, network policy, UDP | [Protection and network](https://docs.rytiv.com/reference/api/protection-and-network.md) |
| Mail | `/v1/emails`, `/v1/mail/*` | [Mail](https://docs.rytiv.com/reference/api/mail.md) |
| Webhooks | `/v1/webhooks` | [Webhooks](https://docs.rytiv.com/reference/api/webhooks.md) |
| MCP | `POST /v1/mcp` | [MCP](https://docs.rytiv.com/reference/api/mcp.md), [MCP tools](https://docs.rytiv.com/reference/mcp-tools.md) |
| OAuth and device login | `/v1/oauth/*`, `/v1/device/*` | [OAuth and device login](https://docs.rytiv.com/reference/api/oauth.md) |
