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

# Generations

> The lifecycle of a generation, how to poll, and how idempotency keys protect you from double charges.

Every submit endpoint (`/v1/images`, `/v1/videos`, `/v1/audio`) returns the same object, and
`GET /v1/generations/{id}` returns its current state.

```json theme={null}
{
  "id": "6f1c2d3e-9a0b-4c7d-8e1f-2a3b4c5d6e7f",
  "type": "video",
  "status": "completed",
  "model": "seedance-2-5-fal",
  "prompt": "A slow aerial shot over a misty pine forest at dawn",
  "outputs": [
    { "url": "https://…", "asset_id": "9c1d…", "content_type": "video/mp4", "width": 1280, "height": 720, "duration_seconds": 5.0 }
  ],
  "credits_charged": 39.6,
  "error": null,
  "idempotency_key": "forest-intro-v3",
  "created_at": "2026-10-03T10:00:00Z",
  "completed_at": "2026-10-03T10:00:41Z"
}
```

## Status

| `status` | Meaning |
| - | - |
| `pending` | Accepted and running. Credits are reserved. `outputs` is empty. |
| `completed` | Done. `outputs` holds one entry per generated file. `credits_charged` is final. |
| `failed` | Did not finish. `error.code` and `error.message` explain; reserved credits are returned. |

`type` is the media kind (`image`, `video`, `audio`). Each output carries an `asset_id`: pass it
as a reference input (`first_frame`, `reference_images`, `motion_video`, …) to build on it in the
next generation without uploading anything.

## Polling

Poll `GET /v1/generations/{id}` every 2–5 seconds and back off a little on each round. Images
usually finish in under a minute; video can take a few minutes depending on the model and
duration. Polling counts against your per-key rate limit, so a 2-second interval is plenty.

Output URLs point at Ekly's storage and are not permanent. Download the file promptly or copy it
to your own bucket; do not store the URL as the asset.

## Idempotency

Send an `Idempotency-Key` header (up to 128 characters) with every submit. If the request is
retried with the same key and the same body, you get the original generation back and nothing is
charged again. The same key with a **different** body returns `409 conflict`, which protects you
from accidentally reusing a key across unrelated requests. Keys never expire and are scoped to
your organization.

The body field `idempotency_key` does the same thing; if you send both, they must match or the
request is rejected with `422`.

## Listing

`GET /v1/generations` lists generations created by the key's creator in your organization,
newest first. Page with `next_cursor` → `?after=`, and filter by `status`, `type`, `model` or
`created_after`. Cursors stay valid while new generations arrive.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.