---
title: Agent endpoints
description: Every /v1/agents endpoint for cloud agents: create agents for your users, settings, start and stop, run tasks over the API, files and share links, open the web page, agent tokens, and brandings for white-labeled agents.
layout: reference
---

The cloud agents API lives under `https://lovelyrouter.com/v1/agents`. Authenticate with an API key from the console as `Authorization: Bearer $LOVELY_API_KEY` ([Authentication](/docs/authentication)), or, for one agent, with an [agent token](#tokens). The keys an agent session gets for itself can't call these endpoints. Bodies are JSON; money is in micro-dollars (1,000,000 = $1). Agents are billed from your prepaid balance; see [Cloud agent pricing](/docs/agents#pricing).

For a walkthrough, start with the [cloud agents overview](/docs/agents).

## The agent object {#agent}

:::params Attributes
- `id` string
  Unique id, `agt_` followed by 24 characters.
- `object` string
  Always `agent`.
- `name` string | null
  Your label.
- `user` string | null
  Your own id for the user this agent serves (1–128 characters: letters, digits, `_ . @ : + -`). Filter the list by it.
- `metadata` object | null
  Your JSON object, up to 4 KB.
- `status` string
  `stopped`, `starting` or `running`.
- `session` object | null
  The open session: `id`, `status`, `size`, `vcpu`, `mem_gib`, `started_at`, `ready_at`, and what it has cost so far: `charge_micros` (sandbox time), `llm_micros` (models and tools) and `total_micros`.
- `url` string
  The agent's web page, `https://agent.lovelyrouter.com/a/<id>/`. Sign in with [Open](#open) or a `ui` [token](#tokens).
- `size`, `idle_minutes`, `max_hours`, `auto_start`
  See [settings](#update).
- `settings` object
  `model`, `tools`, `mcp_servers` (header values shown as `••••••••`), `instructions`.
- `budget_micros` integer | null
  The agent stops, and won't start or run, once `spend.total_micros` reaches it.
- `branding` string | null
  The [branding](#brandings) its page wears (`brd_…`), or `null` for the standard look.
- `white_label` boolean
  `true` when it has a branding: its sandbox is billed at the [white-label rates](/docs/agents/white-label#pricing). The open `session` says the same of itself.
- `spend` object
  `compute_micros` (sandbox time), `llm_micros` (models and tools its sessions used), `total_micros`.
- `workspace` object
  `bytes` stored, `measured_at`.
- `created_at` integer
  Milliseconds since the epoch.
:::

## Create an agent {#create}

::endpoint POST /v1/agents

Creates an agent with its own empty workspace and returns `201` with the [agent object](#agent). Accepts every [setting](#update), plus `"start": true` to start it at once. Up to 1,000 agents per account.

```bash title="cURL" aside
curl https://lovelyrouter.com/v1/agents \
  -H "Authorization: Bearer $LOVELY_API_KEY" \
  -H "content-type: application/json" \
  -d '{"name": "Support bot", "user": "cust-42", "metadata": {"plan": "pro"}, "budget_micros": 5000000}'
```

```python title="Python" aside
API = "https://lovelyrouter.com/v1/agents"
H = {"Authorization": f"Bearer {os.environ['LOVELY_API_KEY']}"}

agent = requests.post(API, headers=H, json={"name": "Support bot", "user": "cust-42"}).json()
```

```js title="Node.js" aside
const API = 'https://lovelyrouter.com/v1/agents';
const headers = { Authorization: `Bearer ${process.env.LOVELY_API_KEY}`, 'content-type': 'application/json' };

const agent = await fetch(API, { method: 'POST', headers, body: JSON.stringify({ name: 'Support bot', user: 'cust-42' }) }).then((r) => r.json());
```

## List agents {#list}

::endpoint GET /v1/agents

Newest first. `?user=cust-42` returns that user's agents; `limit` (≤ 200, default 50); `before` (a `created_at`) pages back, and `next_before` in the response is the value for the next page.

```bash title="cURL" aside
curl "https://lovelyrouter.com/v1/agents?user=cust-42" -H "Authorization: Bearer $LOVELY_API_KEY"
```

## Get, update or delete an agent {#update}

::endpoint PATCH /v1/agents/{id}

`GET` returns the agent; `PATCH` (or `POST`) changes the fields you send; `DELETE` stops it if it runs and deletes it with its workspace, for good. Settings apply from the next start.

:::params Body parameters
- `name`, `user`, `metadata`, `budget_micros`
  As in the [agent object](#agent); `null` clears them.
- `model` string | null
  The default LLM, any chat model id from [`GET /v1/models`](/docs/api/models). `null` = `claude-haiku-5.5`.
- `tools` boolean, default `true`
  Lovely Router's [data tools](/docs/tools) and [browser agent](/docs/browser-agent) as MCP tools (`mcp__tools__*`), billed per use.
- `mcp_servers` array
  Up to 8 remote MCP servers (streamable HTTP): `{"name", "url", "headers"}`. `name` is 1–32 characters of `a-z 0-9 _ -` starting with a letter (`tools` is taken), `url` a public `https://` URL, `headers` up to 10. Header values are stored encrypted and never returned; sending `••••••••` back keeps the stored value.
- `instructions` string | null
  Added to every session, up to 4,000 characters.
- `size` string
  `small`, `medium` or `large` ([sizes](/docs/agents#pricing)).
- `idle_minutes` integer
  5, 15, 30, 60, 120, 240, 480 or 1440.
- `max_hours` integer
  1, 2, 4, 8, 12 or 24.
- `auto_start` boolean, default `true`
  Wake up when called: a stopped agent starts by itself for a [run](#runs), an [open](#open), or a visit to its page. Applies at once.
- `branding` string | null
  A [branding](#brandings) of yours: the agent becomes [white-labeled](/docs/agents/white-label) from its next start, billed at the white-label rates. `null` = the standard look and rates.
:::

```bash title="cURL" aside
curl -X PATCH https://lovelyrouter.com/v1/agents/agt_3f9a1c7e0b2d4e6f8a9b0c1d \
  -H "Authorization: Bearer $LOVELY_API_KEY" \
  -H "content-type: application/json" \
  -d '{"model": "claude-sonnet-5.5", "instructions": "Reply in the user'"'"'s language."}'
```

## Start and stop {#start}

::endpoint POST /v1/agents/{id}/start

Starts the agent; it is `running` a few seconds later (a new agent's first start can take longer). `POST /v1/agents/{id}/stop` stops it and returns the session with its final `charge_micros`. Starting needs a balance that covers 15 minutes of its size (at least $0.25); up to 20 agents run at once per account. [Runs](#runs) start a stopped agent by themselves.

```bash title="cURL" aside
curl -X POST https://lovelyrouter.com/v1/agents/agt_3f9a1c7e0b2d4e6f8a9b0c1d/start -H "Authorization: Bearer $LOVELY_API_KEY"
```

## Run a task {#runs}

::endpoint POST /v1/agents/{id}/runs

The agent API: sends one task to the agent and returns a run. The agent works in its sandbox with its tools and settings, exactly as in its web page; each run's session is saved and shows up there too. A stopped agent is started first when its `auto_start` is on (the default), and the agent stops about 5 seconds after its last run ends, whether the run woke it or it was already running, unless its page was used in that session (it then stops when idle). To keep it running between runs, send `stop_when_done: false`. Works with an API key or an agent token with the `api` scope.

:::params Body parameters
- `input` string, required
  The task, up to 100,000 characters.
- `session_id` string
  Continue this session (from an earlier run's `session_id`). Omitted: a new session.
- `wait` integer
  Hold the request up to this many seconds (≤ 300) for the run to finish.
- `stream` boolean
  Return the run's events as server-sent events instead: `run`, `session`, `status` (turns and steps, with token usage), `tool_call`, `tool_result`, `text`, `final`, then `end` with the finished run.
- `stop_when_done` boolean
  Stop the agent about 5 seconds after its runs are done. Default: `true`, unless the agent's page was used in this session. `false` keeps the agent running (it then stops when idle); `true` stops it after this run even if its page was used before, unless someone uses its page meanwhile.
- `start` boolean, default: the agent's `auto_start`
  Start the agent if it is stopped. With an agent token, only `auto_start` decides: `true` can't start an agent that has it off.
:::

A run lists the files the agent handed over in it as `files`: `path` (relative to the agent's home folder), `description`, `bytes` and `url`, where [Files](#files) downloads it. A file saved outside the home folder isn't kept and has `url: null`. To show one to someone without an API key, make a [share link](#shares).

`GET /v1/agents/{id}/runs` lists the runs since the agent last started, `GET /v1/agents/{id}/runs/{run_id}?wait=30` returns one, `GET …/events` streams it, `POST …/cancel` stops it. Runs are kept while the agent runs; their sessions stay in the workspace. Up to 4 runs work at once per agent, one per session.

```bash title="cURL" aside
curl https://lovelyrouter.com/v1/agents/agt_3f9a1c7e0b2d4e6f8a9b0c1d/runs \
  -H "Authorization: Bearer $LOVELY_API_KEY" \
  -H "content-type: application/json" \
  -d '{"input": "Summarize README.md in three bullets.", "wait": 120}'
```

```python title="Python" aside
run = requests.post(f"{API}/{agent['id']}/runs", headers=H,
                    json={"input": "Summarize README.md in three bullets.", "wait": 120}).json()
follow_up = requests.post(f"{API}/{agent['id']}/runs", headers=H,
                          json={"input": "Now in French.", "session_id": run["session_id"], "wait": 120}).json()
```

```js title="Node.js" aside
const run = await fetch(`${API}/${agent.id}/runs`, {
  method: 'POST',
  headers,
  body: JSON.stringify({ input: 'Summarize README.md in three bullets.', wait: 120 }),
}).then((r) => r.json());
```

:::aside
```json title="Response"
{
  "id": "run_k3f9a1c7mf0x2",
  "object": "agent_run",
  "agent_id": "agt_3f9a1c7e0b2d4e6f8a9b0c1d",
  "status": "completed",
  "session_id": "session-631cba7f-3095-4f66-b2cb-6786ff1f951c",
  "input": "Summarize README.md in three bullets.",
  "output": "- …",
  "error": null,
  "usage": { "input_tokens": 1240, "output_tokens": 96, "cache_read_tokens": 15703, "cache_write_tokens": 0 },
  "steps": 2,
  "tool_calls": 1,
  "files": [
    {
      "path": "deepseek-harness/api-requests/summary.md",
      "description": "README summary",
      "bytes": 412,
      "in_home": true,
      "url": "https://lovelyrouter.com/v1/agents/agt_3f9a1c7e0b2d4e6f8a9b0c1d/files/deepseek-harness/api-requests/summary.md"
    }
  ],
  "started_at": 1792363795275,
  "finished_at": 1792363805502
}
```
:::

## Files {#files}

::endpoint GET /v1/agents/{id}/files/{path}

Lists a folder, or downloads a file, of the agent's home folder. `path` is relative to it; leave it out for the home folder itself. API runs work in `deepseek-harness/api-requests` (their sessions are grouped as *API Requests* in the agent's web page); sessions started in the web page work in `deepseek-harness/default-workspace` unless you open another folder. While the agent runs, files are read live (`"live": true`); while it is stopped they are as saved at its last stop, and reading them doesn't start it. Works with an API key or an agent token with the `api` scope.

A folder returns `{"object": "list", "path", "live", "data", "truncated"}`: up to 1,000 entries, folders first, each with `name`, `path`, `type` (`file` or `dir`), `bytes`, `modified_at` and `url`. A file returns its bytes as an attachment with its content type.

```bash title="cURL" aside
curl https://lovelyrouter.com/v1/agents/agt_3f9a1c7e0b2d4e6f8a9b0c1d/files/deepseek-harness/default-workspace \
  -H "Authorization: Bearer $LOVELY_API_KEY"

curl -O https://lovelyrouter.com/v1/agents/agt_3f9a1c7e0b2d4e6f8a9b0c1d/files/deepseek-harness/api-requests/summary.md \
  -H "Authorization: Bearer $LOVELY_API_KEY"
```

## Share links {#shares}

::endpoint POST /v1/agents/{id}/shares

Makes a link that opens a file, or a folder as a website, for anyone who has it, with no sign-in. The link keeps working while the agent is stopped. A folder's link opens its `index.html` (or a list of its files when it has none), and the page loads the folder's other files by relative path. A file's link ends with its name. Returns `201` with `{"object": "agent_share", "agent_id", "path", "type", "url", "expires_at"}`.

:::params Body parameters
- `path` string, required
  A file or folder in the agent's home folder, as in [Files](#files).
- `expires_in` integer, default `604800`
  Seconds the link works: 60 to 31,536,000 (a year). Default: a week.
:::

`POST /v1/agents/{id}/shares/revoke` turns off every link of the agent at once. A link also ends when its agent is deleted, or when the API key or agent token that made it is revoked: an agent token with the `api` scope can make links too, and they end with the token. Only the account can turn links off.

Shared pages run sandboxed: scripts, forms and resources from other sites work, cookies and browser storage don't, and the page can't reach Lovely Router or other agents.

```bash title="cURL" aside
curl https://lovelyrouter.com/v1/agents/agt_3f9a1c7e0b2d4e6f8a9b0c1d/shares \
  -H "Authorization: Bearer $LOVELY_API_KEY" \
  -H "content-type: application/json" \
  -d '{"path": "deepseek-harness/default-workspace/site", "expires_in": 86400}'
```

:::aside
```json title="Response"
{
  "object": "agent_share",
  "agent_id": "agt_3f9a1c7e0b2d4e6f8a9b0c1d",
  "path": "deepseek-harness/default-workspace/site",
  "type": "dir",
  "url": "https://agent.lovelyrouter.com/s/eyJrIjoic2hyIiwi…/",
  "expires_at": 1792450195275
}
```
:::

## Open the web page {#open}

::endpoint POST /v1/agents/{id}/open

Returns `url`, a link valid for 60 seconds that opens the running agent's web page, signed in. The sign-in lasts 7 days, or until the key is revoked. A stopped agent with `auto_start` on is started first (its page shows *Starting…* until it is ready); otherwise `409`.

## Agent tokens for your users {#tokens}

::endpoint POST /v1/agents/{id}/tokens

Creates a token for one agent, to give to your own user, so they never see your API key. `scopes`: `api` lets the token use this agent's [runs](#runs) and [files](#files), make [share links](#shares), and read its status; `ui` makes `url`, a link that opens the agent's web page (reusable until the token expires). Give `api` only to users who should not see the web page. `expires_in`: 60 seconds to 30 days (default 3,600). The token stops working when it expires, when you revoke the agent's tokens, when you delete the agent, or when you revoke the API key that created it.

`POST /v1/agents/{id}/tokens/revoke` revokes every token of the agent issued so far (links already opened stop within a minute).

```bash title="cURL" aside
curl https://lovelyrouter.com/v1/agents/agt_3f9a1c7e0b2d4e6f8a9b0c1d/tokens \
  -H "Authorization: Bearer $LOVELY_API_KEY" \
  -H "content-type: application/json" \
  -d '{"scopes": ["api"], "expires_in": 3600}'
```

:::aside
```json title="Response"
{
  "object": "agent_token",
  "agent_id": "agt_3f9a1c7e0b2d4e6f8a9b0c1d",
  "scopes": ["api"],
  "token": "agtok_eyJrIjoiYWd0Iiwi…",
  "expires_at": 1792367395275,
  "url": null
}
```
:::

## Sessions {#sessions}

::endpoint GET /v1/agents/{id}/sessions

The agent's last 50 sandbox sessions, the open one included, with what each cost: `charge_micros` (sandbox time), `llm_micros` (the models and tools its key used) and `total_micros`. A session's `stop_reason` is `user`, `idle`, `done` (the run that woke it ended), `balance`, `budget`, `max_time`, `exited`, `error`, `deleted` or `admin`.

## Brandings {#brandings}

::endpoint POST /v1/agents/brandings

Saved looks for [white-labeled agents](/docs/agents/white-label). `POST` creates one (`201`); `GET /v1/agents/brandings` lists yours (newest first, up to 50); `GET`, `PATCH` (or `POST`) and `DELETE /v1/agents/brandings/{id}` read, change and delete one. A `PATCH` changes the fields you send; agents that use the branding pick the change up at their next start. Deleting a branding that agents use returns `409`. Agent tokens can't call these endpoints.

:::params Body parameters
- `agent_name` string, required
  The name your users see on the page and the agent uses for itself: 1–40 letters, digits, spaces and `. _ -`, starting with a letter or digit.
- `name` string
  Your label, up to 60 characters. Default: `agent_name`.
- `logo` string | null
  A PNG, JPEG, WebP or SVG image as a base64 `data:` URI, up to 64 KB. `null` = a monogram: the first letter of `agent_name` on the accent.
- `scheme` string, default `dark`
  `dark` or `light`.
- `colors` object
  `#rrggbb` values: `accent`, `bg` (background), and optionally `accentSoft` (links), `onAccent` (text on the accent), `surface`, `surface2`, `surface3` (panels, raised areas, borders), `ink`, `ink2`, `ink3` (text, secondary, muted). Colors you leave out are derived from `scheme`, `accent` and `bg`. A `PATCH` that changes only `scheme` derives a new palette from the accent.
- `font` string | null
  Text font: `Inter`, `Geist`, `Roboto`, `Open Sans`, `Lato`, `Poppins`, `Montserrat`, `Nunito`, `DM Sans`, `Manrope`, `Plus Jakarta Sans`, `Space Grotesk`, `Work Sans`, `IBM Plex Sans`, `Source Sans 3`, `Noto Sans`, `Outfit`, `Figtree`, `Rubik` or `Lora`. `null` = the page's own.
- `mono_font` string | null
  Code font: `JetBrains Mono`, `Geist Mono`, `IBM Plex Mono`, `Fira Code`, `Source Code Pro`, `Roboto Mono`, `DM Mono` or `Space Mono`.
- `welcome` string | null
  The headline over the first message box, up to 80 characters. `null` = *What can I do for you?*
:::

The branding object has these fields with every color filled in, plus `id` (`brd_` and 24 characters), `object` (`agent_branding`), `agents` (how many agents use it; in lists and reads), `created_at` and `updated_at`.

```bash title="cURL" aside
curl https://lovelyrouter.com/v1/agents/brandings \
  -H "Authorization: Bearer $LOVELY_API_KEY" \
  -H "content-type: application/json" \
  -d '{"agent_name": "Nova Assistant", "scheme": "light", "colors": {"accent": "#0d9488"}, "font": "Inter", "welcome": "How can Nova help today?"}'
```

:::aside
```json title="Response"
{
  "id": "brd_6c1f0a9e3b7d2c4e8f5a1b0d",
  "object": "agent_branding",
  "name": "Nova Assistant",
  "agent_name": "Nova Assistant",
  "logo": null,
  "scheme": "light",
  "colors": {
    "accent": "#0d9488", "accentSoft": "#0b7f75", "onAccent": "#ffffff",
    "bg": "#f7f8fa", "surface": "#ffffff", "surface2": "#f0f1f3", "surface3": "#e6e8ea",
    "ink": "#14161b", "ink2": "#5d5e62", "ink3": "#86878b"
  },
  "font": "Inter",
  "mono_font": null,
  "welcome": "How can Nova help today?",
  "agents": 0,
  "created_at": 1792367395275,
  "updated_at": 1792367395275
}
```
:::

## Custom domain {#domain}

::endpoint PUT /v1/agents/brandings/{id}/domain

Connects your own domain to a branding ([how it works](/docs/agents/white-label#domain)); `201` with the domain object. `GET` reads it (add `?check=1` to ask for the latest status now), `DELETE` disconnects it. One domain per branding. Connecting needs a balance of at least one day's price; the domain costs $0.50 per day while connected, starting the day it is connected, with no refund for a day already started. The branding object carries the same object as `domain` (or `null`).

:::params Body parameters
- `hostname` string, required
  A domain you own, such as `agents.yourcompany.com` (a pasted URL is fine).
:::

:::params The domain object
- `hostname` string
  In lower case.
- `status` string
  `pending` (waiting for your DNS record or the certificate), `active` (serving your agents), or `error` (see `errors`).
- `records` array
  The DNS records to add: `{"type", "name", "value", "purpose"}`. `purpose` `route` is the `CNAME` that sends the domain to us (required); `ownership` and `certificate` are TXT records that let the certificate be issued before the domain points at us (optional).
- `cname_target` string
  Where the `CNAME` points.
- `errors` array
  What is still missing, while pending.
- `price_day_micros` integer
  The price per day.
- `created_at`, `active_at`, `checked_at` integer | null
  Milliseconds since the epoch.
:::

```bash title="cURL" aside
curl -X PUT https://lovelyrouter.com/v1/agents/brandings/brd_6c1f0a9e3b7d2c4e8f5a1b0d/domain \
  -H "Authorization: Bearer $LOVELY_API_KEY" \
  -H "content-type: application/json" \
  -d '{"hostname": "agents.yourcompany.com"}'
```

## Errors {#errors}

Errors use the shape of the rest of the API: `{"error": {"type", "message"}}`.

| Status | Type | When |
| --- | --- | --- |
| 400 | `invalid_request_error` | A field is invalid (the message says which) |
| 401 | `authentication_error` | Missing, invalid, revoked or expired key or token |
| 402 | `insufficient_quota` | The balance doesn't cover 15 minutes of the size, or the agent's budget is used up |
| 403 | `permission_error` | A token without the scope, a token used for something other than its agent's runs and files, or an agent's own key |
| 404 | `not_found` | No such agent, run, file or folder |
| 409 | `conflict` | Already running (start), stopped with `auto_start` off (open, a run) or a run with `"start": false`, a run already working in that session, or deleting a branding that agents use |
| 429 | `rate_limit_exceeded` | 20 agents already running, or 4 runs already working on this agent |
| 502 | `api_error` | The sandbox couldn't start; you are not charged. Try again |
| 503 | `service_unavailable` | Cloud agents are not available right now, or the agent is still starting |
