# Give an agent a job that runs on its own

A run is a job. You give an agent a goal and it works on it outside your request, for as many rounds as it needs, until it declares the job done or something stops it. Nobody has to keep a connection open. Use a run when the work is longer than a conversation: reconciling a statement, filling in a batch, preparing a report.

## Create a run

The agent must be active, created through the API or a template (not an internal one of the dashboard) and have the `api` channel. Your key needs `runs:write`.

```bash title="cURL"
curl -X POST "https://api.lelemon.cl/v2/agents/agt_5RvP2aKq9XwLmN3tYbZc01/runs" \
  -H "Authorization: Bearer $LELEMON_API_KEY" \
  -H "Idempotency-Key: reconcile-2026-08" \
  -H "Content-Type: application/json" \
  -d '{
    "goal": "Reconcile the August bank statement and tell me what is left pending.",
    "actor": { "id": "user_8841", "role": "staff" },
    "limits": { "maxRounds": 20, "maxMinutes": 20 }
  }'
```

It answers `201` right away with the run in `running`. Repeat the request with the same `Idempotency-Key` and you get the run already created, not a second one. `?dryRun=true` runs the same checks and shows the run as it would be created, without queueing anything.

| Field | Meaning |
| --- | --- |
| `goal` | What the agent has to achieve, up to 8,000 characters. The agent reads it as text from a user. |
| `actor` | The person in your system the agent works for. See [who the run works for](#who-the-run-works-for). |
| `limits` | `maxRounds` and `maxMinutes` for this run. The defaults are 30 rounds and 30 minutes. Above 60 rounds or 120 minutes it is cut to those maximums. What you leave out takes the default. |

## Who the run works for

The run is unattended: the agent uses any action it is granted, including the ones that write, with nobody looking. So the run has to say who it acts for.

There is no signature to compute and no shared secret. Your API key is what vouches for the person. Declare them in `actor`:

- **`actor`** is `{ id, role, name? }`. `id` is the person in your system, `role` is `end_user`, `staff` or `admin`, and `name` is optional: the dashboard shows it as who the run works for. Lelemon does not verify them. It records that your key declared them, and the run carries that to every action and MCP server as the signed actor: `actor.id`, `actor.role` and `actor.declaredBy` (your `key_…`). A key cannot declare a role above its own: a key of a subaccount tops at `staff`, and `403 actor_role_above_key` otherwise.
- **Without `actor`** the run works as your key. The id that travels is the `key_…` of the key.

The run answers with the actor it kept, so you can audit who an overnight job acted for. Give `runs:write` only to systems you already run: a key with it decides which person the agent acts as.

If your agent uses an MCP server that needs to know the person, turn on the [run identity of the server](/guides/actions-and-mcp). Lelemon signs the call to the server with the actor inside; the server verifies Lelemon's signature, not yours.

## Follow it

```bash title="cURL"
curl "https://api.lelemon.cl/v2/runs/run_5RvP2aKq9XwLmN3tYbZc01" \
  -H "Authorization: Bearer $LELEMON_API_KEY"
```

`GET /v2/runs` lists the runs, newest first, filtered by `agentId` or `status`. Read needs `runs:read`.

| `status` | Meaning |
| --- | --- |
| `running` | Working, or waiting in the queue (`startedAt` is `null` until a worker takes it). |
| `waiting_for_answer` | Paused to ask. `question` has it. Answer it. |
| `completed_unverified` | The agent says it finished. Nobody has checked it, so do not read it as done: look at `result.summary` and `result.checks`. |
| `stopped` | The run was cut before the agent declared the job done. `stopReason` says why, see [how a run ends](#how-a-run-ends). `result.partial` keeps the last thing the agent wrote. |
| `failed` | An error. `result.error` says what. With `stopReason: "abandoned"`, nobody answered its question in 30 days. |
| `canceled` | You canceled it. |

`rounds` shows the rounds it has taken so far, against `limits.maxRounds`.

## How a run ends

A run does not end because the agent stops talking. The agent ends it by calling its finish action with a summary and a list of things to check. Only then the run is `completed_unverified`, and `result.summary` and `result.checks` hold what it declared. The finish action is refused until the agent has used at least one tool, so a run cannot end with a summary and no work behind it.

If the agent answers in plain text without calling it, the run tells the agent it has not finished and gives it another round. After four such answers in a row the run stops with `stopReason: "no_conclusion"`. That is what a goal that needs no tools usually ends in: the agent answers, never calls the finish action, and its answer is in `result.partial`. To get `completed_unverified`, word the goal so the agent has to do something with its tools, and say that it must finish when done.

When `status` is `stopped` or `failed`, `stopReason` says why:

| `stopReason` | Status | Meaning |
| --- | --- | --- |
| `rounds` | `stopped` | It used all the rounds of `limits.maxRounds`. |
| `cost` | `stopped` | It reached the spending limit Lelemon sets for every run. |
| `time` | `stopped` | It lasted all of `limits.maxMinutes` since it started. |
| `no_conclusion` | `stopped` | The agent answered four times in a row without calling its finish action. |
| `loop` | `stopped` | The agent kept repeating the same action. |
| `abandoned` | `failed` | It asked a question and nobody answered in 30 days. |

`rounds`, `cost` and `time` are limits. `no_conclusion` and `loop` are not: the agent had room left and did not use it to finish. In every `stopped` run `result.partial` keeps the last thing the agent wrote, so read it before you start the job again. It can be the answer you wanted.

## Webhooks

Do not poll. Subscribe an endpoint to `run.started`, `run.waiting_for_answer`, `run.completed`, `run.stopped`, `run.failed` and `run.canceled`. Each one carries the run in `data.run`, as `GET /v2/runs/{id}` would answer at that moment. They are sent at least once: a run a worker lost halfway can announce `run.started` again. A platform account also gets those of its subaccounts, with `customerId`.

## Answer a question

When the agent needs a person it pauses with `waiting_for_answer`, and `run.waiting_for_answer` fires.

```bash title="cURL"
curl -X POST "https://api.lelemon.cl/v2/runs/run_5RvP2aKq9XwLmN3tYbZc01/answer" \
  -H "Authorization: Bearer $LELEMON_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "answer": "Use the checking account.", "actor": { "id": "user_9020", "role": "staff" } }'
```

The run goes back to `running` with your answer as context of its next round, and the agent sees who answered (`actor.id`, or your key without `actor`). A question is answered once: a run that is not paused answers `409 run_not_waiting`. Someone from your team can also answer in the dashboard, under the agent, in **Activity**.

## Cancel it

`POST /v2/runs/{id}/cancel`. If nobody is running it, it ends `canceled` at once. If a worker is running it, `cancelRequested` turns `true` and it ends when its next round starts: the action it was doing finishes. A run that already ended answers `409 run_finished`.

## What keeps it safe

- **Limits.** Rounds, minutes and the spending limit Lelemon sets for every run are checked on every round and the server maximums cannot be raised by API. A run that crosses one ends `stopped` with `stopReason` `rounds`, `cost` or `time` and its partial result.
- **Approvals.** An action behind an [approval policy](/guides/approvals) does not run inside a run either: it waits for a person. The agent is told the action is in review and keeps working on the rest.
- **Subaccounts.** With the `Lelemon-Subaccount` header the run belongs to the subaccount and uses its agents, and its actor ceiling is `admin`.
- **No data in the log.** The goal, the question and the result are never logged.
