> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mireye.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# POST /v1/runs

> Submit a job, get a run_id immediately, poll or stream its progress.

## When to use this

When you don't want to hold the connection open. `POST /v1/runs` accepts a
job, returns **202 with a `run_id` immediately**, and executes in the
background — you read its state whenever you like:

* **Poll**: `GET /v1/runs/{run_id}` — status, progress, and (once terminal)
  the result. This is the source of truth.
* **Stream**: `GET /v1/runs/{run_id}/events` — server-sent events; a `status`
  frame on every change and a terminal `final` frame carrying the full run.
  A convenience over polling, not a second contract.

One kind today: **`fetch_batch`** — the exact
[`/v1/fetch/batch`](/api-reference/fetch-batch) request, same validation, same
limits, same result body. More kinds hang off this spine as they ship.

## Submit

```bash theme={null}
curl -s https://api.mireye.com/v1/runs \
  -H "Authorization: Bearer $MIREYE_API_TOKEN" \
  -H 'content-type: application/json' \
  -d '{
    "kind": "fetch_batch",
    "request": {
      "locations": [{"lat": 29.7604, "lng": -95.3698},
                    {"address": "350 5th Ave, New York, NY 10118"}],
      "fields": ["elevation", "coast_distance_m"]
    }
  }' | jq
```

```json theme={null}
{
  "run_id": "run_a1b2c3d4e5f6",
  "status": "queued",
  "poll": "/v1/runs/run_a1b2c3d4e5f6",
  "events": "/v1/runs/run_a1b2c3d4e5f6/events"
}
```

**Caller mistakes fail at submit, not in the background.** An unknown field
name, a missing field selection, or an oversize batch raises the same error
the synchronous endpoint would (`400 fields_unknown`, `422`, …) on the POST
itself — a run never exists only to fail on something validation could have
caught.

## Poll

```bash theme={null}
curl -s https://api.mireye.com/v1/runs/run_a1b2c3d4e5f6 \
  -H "Authorization: Bearer $MIREYE_API_TOKEN" | jq
```

```json theme={null}
{
  "run_id": "run_a1b2c3d4e5f6",
  "kind": "fetch_batch",
  "status": "running",
  "created_at": "2026-07-27T09:00:00+00:00",
  "updated_at": "2026-07-27T09:00:04+00:00",
  "expires_at": "2026-08-26T09:00:00+00:00",
  "progress": {"done": 1, "total": 2},
  "request": {"…": "as submitted"},
  "result": null,
  "error": null
}
```

`status` walks `queued → running → done | failed`. Once `done`, `result`
carries exactly the body the synchronous endpoint would have returned. Once
`failed`, `error` carries `{error, message, retryable}` — a
`run_interrupted` failure (the worker restarted mid-run, e.g. during a
deploy) is always retryable: resubmit the same request.

## Stream

```bash theme={null}
curl -sN https://api.mireye.com/v1/runs/run_a1b2c3d4e5f6/events \
  -H "Authorization: Bearer $MIREYE_API_TOKEN"
```

```
event: status
data: {"status": "running", "progress": {"done": 1, "total": 2}}

event: final
data: {"run_id": "run_a1b2c3d4e5f6", "status": "done", "result": {…}, …}
```

The stream ends at the `final` frame. A run that finished before you
connected skips straight to `final`. Streams are capped at 15 minutes
(`events_timeout` error frame) — past that, poll.

## Artifacts: the result as a file

Once a run is `done`, download its result instead of parsing it:

```bash theme={null}
curl -sOJ https://api.mireye.com/v1/runs/run_a1b2c3d4e5f6/artifacts/csv \
  -H "Authorization: Bearer $MIREYE_API_TOKEN"
```

* **`/artifacts/csv`** — one row per location, index-aligned: identity
  columns (`index, ok, lat, lng, source, error`), one column per field
  carrying the **value** (empty when absent or failed), and a
  `failed_fields` column naming what to distrust. Spreadsheet-ready.
* **`/artifacts/geojson`** — a `FeatureCollection`, one `Point` per location
  (GeoJSON `[lng, lat]` axis order), field values as `properties`. A failed
  location keeps its slot as a null-geometry feature, so the collection
  stays index-aligned too.

Artifacts are **rendered on read from the stored result, never stored** — so
they exist exactly as long as the run does (30 days), and there is no second
copy on its own retention clock. A run that is still `queued`/`running`
returns `409 run_not_ready` (retryable); a `failed` run returns
`409 run_failed` (resubmit). PDF is deliberately not offered here: branded
report generation lives in the delivery tooling, not the serving API.

## Ownership, retention, errors

* **A run belongs to the account that submitted it.** Anyone else's `run_id`
  — and any unknown id — reads as `404 run_not_found`: a run id does not
  leak whether someone else's job exists.
* **Runs expire 30 days after submission** and then read as `404`. This is
  the same clock as every other address-holding store: a `fetch_batch`
  request can contain street addresses, and the disclosed 30-day retention
  applies to run documents too.
* **A batch location's failure is inside the result** (`ok: false` entries),
  exactly as on the synchronous endpoint — a failed *location* never fails
  the *run*.
