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

# Async pattern

> How analyses and scorings lifecycles work

<Note>
  This guide assumes you've set `$LYMO_KEY`, `$LYMO_BASE`, and `$VIDEO` as in the [quickstart](/en/guides/quickstart). `$LYMO_BASE` stops at `/platform`; versioned paths go under `/v1`.
</Note>

Long-running work in the Platform API is modeled as a **resource that owns its status**, not as opaque jobs. There are two such resources today:

* `POST /v1/videos/:id/analyses` — analyze a video
* `POST /v1/deals/:id/scorings` — score a deal

Both endpoints return `201` immediately with `status: "processing"` and a stable `id`. You then poll the resource or subscribe to a webhook for completion.

## Lifecycle

```
POST   /v1/videos/:id/analyses           → 201 { id, status: "processing", ... }
GET    /v1/videos/:id/analyses/:id       → 200 { id, status: "processing" | "completed" | "failed", ... }
GET    /v1/videos/:id/analyses           → 200 { data: [history ...] }
```

## Status values

External status collapses several internal workflow states into three values:

| Status       | Meaning                                               |
| ------------ | ----------------------------------------------------- |
| `processing` | Work is queued or in flight                           |
| `completed`  | Finished successfully — `result` is populated         |
| `failed`     | Terminal failure — `error: { code, message }` present |

Once a resource reaches `completed` or `failed`, it does not transition again.

## Reading the completed result

When an analysis or scoring reaches `completed`, the same GET endpoint returns a `result` field alongside the existing status metadata. The full per-field schema is in the **API Reference** tab; here's what to expect at a glance:

* **Analyses** — `result` surfaces the summary sections, checklist evaluation (when enabled on the org), and sales events produced by the run. You can also fetch these piecemeal via `GET /v1/videos/:id/summary`, `.../checklist`, `.../events`.
* **Scorings** — `result` surfaces the computed score factors for the deal. `GET /v1/deals/:id` returns the same factors on the deal itself.

On `failed`, an `error: { code, message }` object replaces `result`. `code` is a stable machine-readable string you can switch on; `message` is human-readable and may change between versions.

Typical end-to-end latency is on the order of minutes — polling once every 5–10 seconds is more than enough. Work that gets genuinely stuck is driven to `failed` by internal timeouts; the API itself does not hold a resource in `processing` indefinitely.

## Polling

```bash theme={null}
ANALYSIS=$(curl -s -X POST "$LYMO_BASE/v1/videos/$VIDEO/analyses" \
  -H "Authorization: Bearer $LYMO_KEY" | jq -r .data.id)

while true; do
  STATUS=$(curl -s "$LYMO_BASE/v1/videos/$VIDEO/analyses/$ANALYSIS" \
    -H "Authorization: Bearer $LYMO_KEY" | jq -r .data.status)
  [ "$STATUS" != "processing" ] && break
  sleep 5
done

echo "Analysis finished with status: $STATUS"
```

Poll every 5 seconds. A `429` response includes a `Retry-After` header — honor it rather than retrying immediately.

## Idempotency

POST requests accept an `Idempotency-Key` header. The API caches the first 2xx response for 24 hours, keyed by `(organization, method, path, key, body-hash)`, and returns it verbatim on retry:

```bash theme={null}
curl -X POST "$LYMO_BASE/v1/videos/$VIDEO/analyses" \
  -H "Authorization: Bearer $LYMO_KEY" \
  -H "Idempotency-Key: $(openssl rand -hex 16)"
```

Retrying with the same key and a different body is rejected on cache key mismatch — the key is effectively bound to the exact request shape, so a replay can't get back a response for a different payload.

<Warning>
  A cached response is returned verbatim even if the underlying resource has since been deleted or re-processed. Send a fresh `Idempotency-Key` to force a new request. Keys longer than 256 characters return `400 bad_request`.
</Warning>

Use idempotency keys whenever a network retry could cause duplicate work — for example, when a client-side timeout ambiguously fails a POST.

## Subscribing instead of polling

For production workloads, register a webhook and let Lymo notify you:

* `video.analysis.completed`
* `video.analysis.failed`

<Info>
  Deal scoring events (`deal.scoring.completed`, `deal.scoring.failed`) are declared in the schema so SDK types remain stable, but the publisher isn't wired up yet — subscribing today produces no deliveries. They'll be enabled in a future release.
</Info>

See [Webhooks](/en/guides/webhooks) for registration, signature verification, and the full event list.
