# Run

```text
POST /v1/run
```

Execute a capability. The maximum billable amount is reserved before the provider call, then settled to the actual charge on success or released on failure. The request waits up to `wait_seconds` (default 30, max 120) for the result and answers `200` with the finished run; a run still executing answers `202` with the same payload (`done: false`) to poll at `GET /v1/runs/{run_id}?wait=60`. Send `"async": true` to get the `202` immediately. A run that would breach the organization's daily spend guard ends as `blocked` with error `budget_exceeded` before any money moves.

## Headers

| Header | Required | Notes |
| --- | --- | --- |
| `Authorization` | yes | `Bearer rg_test_...` or `Bearer rg_live_...` |
| `Idempotency-Key` | recommended | any unique string; makes the call safe to retry |

## Request

```json
{
  "capability": "contact.find",
  "input": {"first_name": "Alex", "last_name": "Rivera", "company_domain": "example.com"},
  "routing": {"provider": "auto", "strategy": "best_value", "max_cost": "0.10", "allow_fallback": true},
  "response": {"include_raw": false}
}
```

| Field | Type | Default | Notes |
| --- | --- | --- | --- |
| `capability` | string | required | from [discover](/docs/api/discover) |
| `input` | object | required | must match the capability's `input_schema` from [inspect](/docs/api/inspect) |
| `routing.provider` | string | `"auto"` | `auto`, or a provider slug to pin |
| `routing.strategy` | string | `"best_value"` | `cheapest`, `fastest`, `highest_success_rate`, `best_value` |
| `routing.max_cost` | string | none | USD cap; offers above it are excluded before the call |
| `routing.allow_fallback` | bool | `true` | set `false` to disable waterfall failover |
| `response.include_raw` | bool | `false` | include the provider's raw payload alongside the normalized result |

## Response

```json
{
  "run_id": "run_...",
  "status": "succeeded",
  "capability": "contact.find",
  "provider": "...",
  "routing_reason": "auto: best_value picked ...",
  "billing": {"currency": "USD", "quoted": "0.05", "charged": "0.05", "platform_funded": true},
  "timing": {"duration_ms": 842},
  "attempts": [
    {"provider": "...", "outcome": "provider_timeout", "duration_ms": 8000, "detail": "..."},
    {"provider": "...", "outcome": "succeeded", "duration_ms": 842, "detail": null}
  ],
  "result": {"...": "..."},
  "request_id": "req_...",
  "created_at": "2026-09-02T09:30:00+00:00"
}
```

`result` is present on success; `error` (`{code, message}`) is present on failure. `billing.charged` is what actually settled: `null` or lower than `quoted` when the reservation was released or partly released.

## Run lifecycle

```text
created → reserved → running → succeeded | no_match | failed | timed_out
```

A run still in `created`, `reserved` or `queued` can be [cancelled](/docs/api/runs). `no_match` means at least one provider answered authoritatively that there is no result; whether that bills depends on the capability's stated `billing` conditions.

## Waterfall failover

With `provider: "auto"`, a provider error, timeout or unbilled no-match falls through to the next-best provider automatically, up to 3 attempts. Only the provider that delivers is charged; every attempt appears in `attempts` with its outcome. `max_cost` caps every attempt, and a pinned provider never falls back.

## Idempotency

Replaying the same `Idempotency-Key` returns the original run, marked `Idempotent-Replay: true`, without executing or billing again. Use it on every retry loop.

## Errors you should handle

| Code | Meaning |
| --- | --- |
| `insufficient_balance` (402) | reservation exceeds available balance: [top up](/docs/api/wallet) |
| `cost_limit_exceeded` (402) | no offer fits under `routing.max_cost` |
| `provider_not_available` (409) | no live provider for this capability yet (production) |
| `no_match` (404) | the providers found nothing; billed only if the capability bills no-matches |
| `provider_timeout` (504), `provider_error` (502) | all attempts failed; nothing charged |

Full list: [Errors](/docs/api/errors).
