---
title: Run your first paid job
description: Check your balance, inspect an endpoint, run a job over the CLI, MCP or REST, read the result and the receipt, and retry safely with an idempotency key.
---

This page walks through one paid run, start to end. You need a token with the `runs.execute` scope and
a balance above the run's price. A new workspace starts at $0, so top up before your first run.

1. **Check your balance**

    <CodeGroup>

    ```bash CLI
    looot balance
    ```

    ```json MCP
    // call the balance tool
    ```

    </CodeGroup>

    A new workspace starts at $0 and there is no trial credit. If the output shows a top-up line,
    add at least the minimum it names first, then come back. See
    [Before your first run](/money#before-your-first-run).

2. **Inspect the endpoint or job**

    Inspect an endpoint id your search returned. The answer has its exact input schema, output schema,
    price formula and estimated maximum cost. `looot inspect job:<id>` is not supported yet; run
    the job directly and read `requestedJob` on the result to see which endpoint was picked.

3. **Run it**

    <CodeGroup>

    ```bash CLI
    looot run job:people.email.verify --input '{"email":"jane.doe@example.com"}' --wait
    ```

    ```json MCP
    {
      "endpointId": "job:people.email.verify",
      "input": { "email": "jane.doe@example.com" },
      "idempotencyKey": "<new unique key>",
      "wait": 20
    }
    ```

    ```bash REST
    curl -X POST "https://api.looot.ai/v1/runs?wait=20" \
      -H "Authorization: Bearer $LOOOT_TOKEN" \
      -H "Content-Type: application/json" \
      -d '{"endpointId":"job:people.email.verify","input":{"email":"jane.doe@example.com"},"idempotencyKey":"<key>"}'
    ```

    </CodeGroup>

    `endpointId: "job:<job id>"` runs the job: looot picks the first provider that can run for
    your workspace and accepts your input, and reports the pick and why in `requestedJob`. You can
    also run a specific endpoint id directly. `wait` (CLI: `--wait`, REST: `?wait=`) blocks until
    the run finishes, up to 60 seconds, and returns the finished run inline; past the window, or
    with `wait` omitted, you get the queued or running run back and follow it with `runs get`.
    [`idempotencyKey`](/concepts/idempotency) is required on REST; the CLI makes one for you (`looot-<uuid>`) and prints it
    on stderr if you don't pass one.

4. **Read the run**

    <CodeGroup>

    ```bash CLI
    looot runs get <run-id>
    looot runs list
    ```

    ```json MCP
    // runs_get { "runId": "<run-id>" }
    // runs_list { "limit": 20 }
    ```

    </CodeGroup>

    A run reports looot's own status separately from the provider's own response status.
    `runs list` (CLI) and [`runs_list`](/mcp-tools/runs-list) (MCP) take a status filter and page with a cursor.

5. **Read the receipt**

    <CodeGroup>

    ```bash CLI
    looot runs evidence <run-id>
    ```

    ```json MCP
    // runs_evidence { "runId": "<run-id>" }
    ```

    </CodeGroup>

    Every attempt made for the run: its status, the provider's response status, latency, the
    receipt id and the cost. Raw provider headers and bodies are never included.

## Run statuses

| Status | Meaning |
| --- | --- |
| `queued`, `running` | Still in progress. Check again with `runs get`. |
| `pending_provider` | The provider is still working on a long job. The hold stays open until it finishes. |
| `completed` | Done. `result` holds the data and `actualCost` what you were charged. |
| `failed` | The call failed. `error` says why. |
| `blocked` | Refused before it ran, for example [`insufficient_balance`](/errors/rest-errors#insufficient_balance). Nothing was charged. |
| `stopped` | You cancelled it. |
| `reconciliation_pending` | The outcome is uncertain. The hold stays reserved until it is resolved. |

`looot run` exits with code 1 when the finished run is `failed` or `blocked`, so a script can rely
on the exit code.

## What you pay

1. **Hold.** When the run is admitted, looot reserves the endpoint's estimated cost from your
   balance. You see it under `reserved` in `looot balance`.
2. **Settle.** When the provider answers, the hold closes. A completed call is charged its actual
   cost and any unused part of the hold goes back to your balance. For a flat per-call price, the
   charge equals the price [`inspect`](/mcp-tools/inspect) showed.
3. **Failure.** A call that fails at the provider is charged $0 and the hold is released.
4. **Refusal.** A run refused before it starts (not enough balance, bad input, a disabled
   endpoint) never takes a hold and costs nothing.

Prices that scale with the number of results reserve for an upper bound on results. The charge is
the actual cost of what came back, never more than three times the hold. See
[Before your first run](/money#before-your-first-run).

## Idempotency keys: retry without paying twice

Every run needs an idempotency key: `idempotencyKey` in the body, or the `Idempotency-Key`
header. The CLI makes one for you if you don't pass one.

- Running again with the **same key and the same input** returns the original run, marked
  `replayed: true`. It never starts a second run and never charges twice, even while the first
  one is still in flight.
- The **same key with a different input** is refused with `409 idempotency_conflict`.
- After a network error or timeout, retry with the same key: if the first request did reach the
  gateway, you get that run back and no new run starts.

After a run is refused for `insufficient_balance`, top up and retry with a **new** key. The old
key stays attached to the refused run.

<Related />
