# REST API

> jurniti customer REST API — OpenAPI spec, bearer API keys, JSON errors, and how it maps to the CLI and MCP. Host api.jurniti.com.

Part of the jurniti developer docs — canonical page: https://www.jurniti.com/docs/api · full docs index: https://www.jurniti.com/docs/llms.txt

## Call jurniti from an agent

The **jurniti REST API** is the same operation set as the [CLI](/docs/cli-reference) and [MCP](/docs/mcp) — one vocabulary (`whoami`, `vms`, `templates`, `fork`). Use it when you cannot spawn `jurniti mcp` (a remote orchestrator, a language without stdio MCP, a one-shot `curl`).

Machine-readable contract: **[https://www.jurniti.com/openapi.json](/openapi.json)** (OpenAPI 3.1). Production host: `https://api.jurniti.com`.

There is **no free tier**. `POST /v1/api/vms` and `POST /v1/api/templates/{id}/fork` charge the card on file.

## Auth

1. Install the CLI and log in (`jurniti login`) — see [Installation](/docs/installation).
2. Mint a key: `jurniti keys issue` (read-write to provision; read-only to observe).
3. Send it on every call:

```bash
curl -sS https://api.jurniti.com/v1/api/whoami \
  -H "Authorization: Bearer jrn_live_…"
```

Missing or bad keys return **401 JSON**:

```json
{"error":"unauthorized","message":""}
```

A read-only key on a money verb returns **403** `read_only_key`. No card on file returns **402** `card_required`. Unknown/unowned ids return **404** `not_found` (existence-as-secret — do not treat 404 as "retry").

Every error is `Content-Type: application/json` with `{ "error": "<code>", "message": "<human-safe>" }`. Never parse an HTML error page from `api.jurniti.com`.

## Surfaces

| Job | REST | CLI | MCP |
| --- | --- | --- | --- |
| Who am I | `GET /v1/api/whoami` | `jurniti whoami` | `whoami` |
| List harnesses | `GET /v1/api/harnesses` | `jurniti harnesses` | `harnesses_list` |
| Provision (paid) | `POST /v1/api/vms` | `jurniti up` | `vm_provision` |
| List / get VMs | `GET /v1/api/vms` | `jurniti vms ls` | `vms_list` |
| Run a task | `POST /v1/api/vms/{id}/run` | `jurniti run` | `vm_run` |
| List templates | `GET /v1/api/templates` | `jurniti templates ls` | `templates_list` |
| Fork (paid) | `POST /v1/api/templates/{id}/fork` | `jurniti fork` | `template_fork` |

The OpenAPI file gives every operation a unique `operationId`, typed parameters, and the JSON error schema so function-calling clients can import it directly.

## Prefer MCP when you can

If the caller is Claude Code, Codex, or another stdio MCP host on a machine that can run the binary, **mount MCP instead of hand-rolling REST**:

```bash
claude mcp add jurniti -- jurniti mcp
```

Discovery card (how to connect, not a hosted Streamable HTTP server): [/.well-known/mcp.json](https://www.jurniti.com/.well-known/mcp.json).

## Related

  - [OpenAPI spec](/openapi.json): Download the customer API contract.
  - [CLI reference](/docs/cli-reference): Every verb, generated from the binary.
  - [MCP](/docs/mcp): Same verbs as tools in your agent.
  - [Installation](/docs/installation): One-line install of the signed CLI.
