--- title: Generation deletion | Luma Agents description: Delete a finished generation with DELETE /v1/generations/{id} and track the asynchronous deletion task with GET /v1/deletions/{task_id}. --- Deleting a generation is **asynchronous**: `DELETE` accepts the request and returns a deletion task immediately (HTTP 202), then a background worker removes the generation’s content. Track progress by polling the task with `GET /v1/deletions/{task_id}` until it reaches a terminal state. The API Reference documents both routes: [Delete a generation](/api/resources/generations/methods/delete/index.md) and [Get a deletion](/api/resources/deletions/methods/get/index.md). ```bash curl -X DELETE https://agents.lumalabs.ai/v1/generations/$GENERATION_ID \ -H "Authorization: Bearer $LUMA_AGENTS_API_KEY" ``` Both routes require a Luma API key. A request without one (for example from an OAuth-connected client) gets `401 "Missing or invalid API key"`. If generation deletion is not enabled for your API client, a request that passes authentication gets `404 "Not Found"`. These routes are not yet in the SDK; call them over HTTP as shown on this page. ## Delete a generation ```bash curl -X DELETE https://agents.lumalabs.ai/v1/generations/0fa56b9a-1c10-4ad5-8a8c-9d23b9e1f0aa \ -H "Authorization: Bearer $LUMA_AGENTS_API_KEY" ``` **Response (HTTP 202 Accepted):** a `DeletionTask` describing the accepted task. ```json { "id": "d4e5f6a7-b8c9-4012-8def-0123456789ab", "resource_type": "generation", "resource_id": "0fa56b9a-1c10-4ad5-8a8c-9d23b9e1f0aa", "state": "scheduled", "phase": null, "blocked_reason": null, "requested_at": "2026-10-08T12:00:00Z", "updated_at": "2026-10-08T12:00:00Z", "scheduled_at": "2026-10-08T12:00:00Z", "delete_deadline": "2026-10-09T12:00:00Z", "completed_at": null, "overdue": false } ``` The `id` is the **task id** — poll it with `GET /v1/deletions/{task_id}`. `resource_type` is always `"generation"` and `resource_id` is the generation id you asked to delete. A new task starts in `state: "scheduled"` (due to run immediately); a worker then moves it to `queued`, `running`, and finally `completed`. If a deletion task for the generation already exists, you get that task instead — see [Repeating `DELETE`](#repeating-delete) and [Deletions Luma schedules automatically](#deletions-luma-schedules-automatically). ### Deletions Luma schedules automatically Depending on how deletion is configured for your API client, Luma can create a deletion task for a generation when it finishes, without any `DELETE` from you: for example once your client’s retention period has passed, or after a grace period for a failed generation (never longer than your client’s retention period). Such a task covers the same root generations `DELETE` accepts. Its `requested_at` is when the generation finished, it is due at the end of the retention or grace period, and its `delete_deadline` is 24 hours after that. A first `DELETE` for such a generation returns that task (HTTP 202), not a new one: - While the task is active, its `requested_at` becomes the time of your `DELETE`. If the task is `scheduled` or `queued`, it also becomes due now at the latest, with a `delete_deadline` no later than 24 hours after your request; in any other active state (`running`, `retry_wait`, `blocked`) its schedule and deadline stay as they were. - Once the task has `completed` and the generation is gone, `DELETE` returns the completed task rather than `404`. Its `requested_at` and `scheduled_at` are the ones Luma set when the generation finished, and `completed_at` can be days old. ### Repeating `DELETE` While the earlier task is still active (any state other than `completed` or `cancelled`), issuing `DELETE` again for the same generation returns **HTTP 202** with the **same task**. Once the generation itself is gone, a repeat `DELETE` also answers `202` with the most recent task for it rather than `404`. If the earlier task was `cancelled` and the generation still exists, a new `DELETE` starts a **new task with a new `id`**. Always track the `id` from the latest `202`. A caller cannot tell a first acceptance from a repeat by the status code. ### The one distinct refusal: not yet terminal A generation is deletable only once its whole tree is **terminal** (every session in the tree has finished) **and** no session in the tree holds an **unsettled billing reserve**. If you delete a generation that is still running or still settling, you get: **HTTP 409** `GENERATION.NOT_TERMINAL` — *“Generation is still running or settling; delete it after it completes or fails.”* ```json { "detail": { "code": "GENERATION.NOT_TERMINAL", "message": "Generation is still running or settling; delete it after it completes or fails." } } ``` This is the only refusal that distinguishes a generation you own from one you do not. A generation owned by another client, a non-existent id, and a non-UUID path all return the same **HTTP 404 `{"detail": "Generation not found"}`** — except that your own generation already removed by a deletion task answers `202` with its latest task (see [Repeating `DELETE`](#repeating-delete)). If generation deletion is not enabled for your client, a request that passes authentication returns **HTTP 404 `{"detail": "Not Found"}`** before the id is looked at. ## Track the deletion ```bash curl https://agents.lumalabs.ai/v1/deletions/d4e5f6a7-b8c9-4012-8def-0123456789ab \ -H "Authorization: Bearer $LUMA_AGENTS_API_KEY" ``` **Response (HTTP 200):** the same `DeletionTask` shape, now reflecting the task’s current state. ```json { "id": "d4e5f6a7-b8c9-4012-8def-0123456789ab", "resource_type": "generation", "resource_id": "0fa56b9a-1c10-4ad5-8a8c-9d23b9e1f0aa", "state": "completed", "phase": null, "blocked_reason": null, "requested_at": "2026-10-08T12:00:00Z", "updated_at": "2026-10-08T12:02:17Z", "scheduled_at": "2026-10-08T12:00:00Z", "delete_deadline": "2026-10-09T12:00:00Z", "completed_at": "2026-10-08T12:02:17Z", "overdue": false } ``` A task id that is not a UUID, does not exist, belongs to another client, or is not a generation deletion returns **HTTP 404 `{"detail": "Deletion not found"}`** — the route does not say which. If generation deletion is not enabled for your client, it returns **HTTP 404 `{"detail": "Not Found"}`** instead. ### Polling Poll `GET /v1/deletions/{task_id}` for `state` until it is terminal. There is no webhook for deletion completion. A reasonable cadence is a short backoff (a few seconds, then lengthening). ```bash # Poll until the task is completed, cancelled or blocked; only "completed" means the content was removed. for attempt in $(seq 1 60); do body=$(curl -s -w '\n%{http_code}' https://agents.lumalabs.ai/v1/deletions/$TASK_ID \ -H "Authorization: Bearer $LUMA_AGENTS_API_KEY") code=$(printf '%s' "$body" | tail -n1) if [ "$code" != "200" ]; then echo "HTTP $code: $(printf '%s' "$body" | sed '$d')" >&2 exit 1 fi state=$(printf '%s' "$body" | sed '$d' | jq -r .state) echo "state=$state" case "$state" in completed) exit 0 ;; cancelled) echo "deletion cancelled before it completed" >&2 exit 1 ;; blocked) echo "blocked: $(printf '%s' "$body" | sed '$d' | jq -r .blocked_reason)" >&2 exit 1 ;; esac sleep 5 done echo "still $state after 60 attempts" >&2 exit 1 ``` Do **not** poll `GET /v1/generations/{generation_id}` to track deletion. Once a deletion is accepted, the generation’s outputs and then the generation itself are removed while the task is still `running`, so that route can return partial content, or `404`, before the task reaches `completed`. Only `state: "completed"` on the deletion task confirms the removal was verified. ## The deletion lifecycle ### States `state` moves through an asynchronous lifecycle. Every state other than `completed` and `cancelled` is active; those two are terminal. | State | Meaning | | ------------ | ---------------------------------------------------------------------------------------------------------- | | `waiting` | Accepted; not yet scheduled to run. | | `scheduled` | Scheduled to run. A task created by `DELETE` starts here. | | `queued` | Due and waiting for, or claimed by, the worker. | | `running` | A worker is executing the deletion. | | `retry_wait` | The previous run ended before finishing; the task is due to be picked up again. | | `blocked` | Stopped — see `blocked_reason`. Not terminal, but the worker does not retry it (see below). | | `completed` | **Terminal.** The generation’s content was removed. `completed_at` is set. | | `cancelled` | **Terminal.** The deletion was cancelled before its irreversible step began. This API cannot cancel tasks. | `completed` and `cancelled` are terminal: stop polling once you see either. Only `completed` means the content was removed. After `cancelled`, contact support, or issue `DELETE` again — while the generation still exists, that starts a new task. ### Phases `phase` reports the last execution phase the task reached: | Phase | Meaning | | ---------------------- | ----------------------------------------------------------------------------------------------------- | | `stopping` | Confirming every session in the generation’s tree is terminal and holds no unsettled billing reserve. | | `discovering` | Enumerating what the deletion will remove. | | `waiting_for_children` | Waiting for child deletion tasks to remove the generation’s artifacts. | | `deleting` | Removing the generation’s session rows. | | `verifying` | Every removal has run; checking them before completing the task. | `phase` is `null` until the task first runs, and again once it is `completed`. In `queued`, `retry_wait`, `blocked`, and `cancelled` it keeps the phase where the last run stopped. ### Blocked reasons When `state` is `blocked`, `blocked_reason` says why. Some failures are retried automatically, with backoff, while the task stays `running`; others block the task at once. A failure removing one of the generation’s artifacts does not block the generation’s task: it stays active (phase `waiting_for_children`) until that artifact is removed — contact support if a task is still active past its `delete_deadline`. The worker never picks up a `blocked` task. Apart from `dependency_cancelled`, which clears when a replacement child task is attached, a blocked task stays blocked until Luma acts on it — contact support as soon as you see `blocked`. | `blocked_reason` | Meaning | | ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `settlement_pending` | When content removal ran, a session in the tree was still running or still held an unsettled billing reserve. | | `shared_bytes` | Not produced for generation deletions today. | | `fk_dependency` | Not produced for generation deletions today. | | `external_error` | Removing or checking content kept failing after the automatic retries, or a session in the tree was still running or held an unsettled billing reserve when the task started or tried to complete. | | `dependency_cancelled` | A child task this deletion waited on was cancelled. | | `operator_hold` | An internal check refused a step; the task is held for Luma to review. | ### Deadline and `overdue` Each accepted task carries a `delete_deadline` — the time by which Luma targets completion. When `DELETE` creates a new task, its deadline is **24 hours** after that request and the task is due to run immediately (`scheduled_at` ≈ `requested_at`). A repeat `DELETE` returns the existing task with the clocks it already has. A task Luma created automatically is due at the end of its retention or grace period; a first `DELETE` can only move it earlier (see [Deletions Luma schedules automatically](#deletions-luma-schedules-automatically)). `overdue` is `true` once `delete_deadline` has passed without the task completing, or when it completed after the deadline — so a `completed` task can carry `overdue: true` permanently, and a `cancelled` task turns `overdue` once its deadline passes. `overdue` does not change `state`: a `blocked` task stays blocked whether or not it is overdue, so do not wait for `overdue` before contacting support about a `blocked` task. ## What a deletion removes — and what it does **not** ### Removed Deleting a generation removes **the content the generation itself produced and owned**: - The **output artifacts** the generation’s own jobs produced (images, videos, and their storage objects). An output that a job outside the generation also produced is not removed, and stored bytes another live artifact still references are kept. - The **artifacts born into the generation as fresh inputs** (`api_attachment_sessions`) — for example inline or URL inputs you supplied with the request. These artifacts are deleted with the generation **even if something else still references them**; as with outputs, stored bytes another live artifact still references are kept. Files referenced by `file_id` are not in this set. - The **session rows** for the generation tree, torn down in foreign-key order. - **Access** to the generation — child artifacts are removed first, then the session row is hard-deleted while the task is still `running`, so `GET /v1/generations/{id}` returns `404` before the task reaches `completed`. **Billing records are kept.** The credits charged for the generation are not removed when its content is deleted. In `GET /v1/usage` grouped by `tenant_id` or `metadata`, a deleted generation’s credits are reported under `unattributed_credits`, because the session that carried those tags no longer exists. ### Not removed — uploaded files are independent Files you uploaded via `/v1/files` and referenced by `file_id` are **not** deleted with the generation. A file is independent of the generations that reference it: removing a generation never removes the file. This guarantee covers `file_id` references only: inline or URL inputs attached to the generation are deleted with it even if they are reused elsewhere. To remove an uploaded file, delete it directly: ```bash curl -X DELETE https://agents.lumalabs.ai/v1/files/0fa56b9a-1c10-4ad5-8a8c-9d23b9e1f0aa \ -H "Authorization: Bearer $LUMA_AGENTS_API_KEY" ``` See the [Files guide](/guides/files/index.md) for how file deletion works. ## Response shape — `DeletionTask` Both `DELETE /v1/generations/{generation_id}` (`202`) and `GET /v1/deletions/{task_id}` (`200`) return the same envelope. | Field | Type | Meaning | | ----------------- | ----------------- | ------------------------------------------------------------------------------------------------------------- | | `id` | UUID | The deletion task id. Poll `GET /v1/deletions/{id}`. | | `resource_type` | `"generation"` | Always `generation` on this surface. | | `resource_id` | string | The generation id the task deletes. | | `state` | string | The deletion state — see [States](#states). | | `phase` | string \| null | The last execution phase reached — see [Phases](#phases); `null` before the first run and once `completed`. | | `blocked_reason` | string \| null | Why the task is blocked — see [Blocked reasons](#blocked-reasons); `null` unless `state` is `blocked`. | | `requested_at` | timestamp | When your first `DELETE` was accepted; for an automatic task you never deleted, when the generation finished. | | `updated_at` | timestamp | When the task last changed — a state transition or execution progress. | | `scheduled_at` | timestamp \| null | When the task became due to run; ≈ `requested_at` for a task created by `DELETE`. | | `delete_deadline` | timestamp \| null | The target completion deadline (24 h after the request for a task created by `DELETE`). | | `completed_at` | timestamp \| null | Set when `state` is `completed`. | | `overdue` | boolean | `true` when `delete_deadline` passed before completion, or completion came after it. | ## Errors | HTTP | Code | When | | ---- | ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | 202 | — | Deletion accepted: a new task, an idempotent re-accept, or a task Luma created automatically for the generation. | | 401 | — | Missing or invalid API key — see [Error handling](/guides/error-handling/index.md#401--unauthorized). | | 403 | — | The request was made with an admin read-only API key, which cannot call these routes, or your API client is suspended or deactivated. | | 404 | — (`"Not Found"`) | The path did not match a route (for example an empty id), or generation deletion is not enabled for your client. | | 404 | — (`"Generation not found"`) | `DELETE /v1/generations/{generation_id}`: the id is not a UUID, or the generation is not yours, does not exist, or is not a root generation (for example a child session). Your own generation already removed by a deletion task answers `202` with that task instead. | | 404 | — (`"Deletion not found"`) | `GET /v1/deletions/{task_id}`: the id is not a UUID, or the task is not yours, not a generation deletion, or does not exist. | | 409 | `GENERATION.NOT_TERMINAL` | Your own generation is still running or still holds an unsettled reserve. | | 429 | — | Rate limited — see [Error handling](/guides/error-handling/index.md#429--too-many-requests). | A `404` carries no `code` field, only `detail`. Within each route the not-found body is the same whether the id is foreign, missing, or malformed, so a caller cannot tell another client’s id from one that does not exist. ## FAQ **Can I delete a generation that is still running?** No. `DELETE` returns `409 GENERATION.NOT_TERMINAL` until every session in the generation’s tree is terminal and no session holds an unsettled billing reserve. Poll the generation to completion (or failure) first, then delete; a `409` right after completion means billing is still settling, so retry after a short delay. **What happens if I call `DELETE` twice?** While the first task is active, or once the generation is gone, the second call returns `202` with the same task. Even a first call can return an existing task, active or `completed`, if Luma had already scheduled the generation for deletion — see [Deletions Luma schedules automatically](#deletions-luma-schedules-automatically). If the first task was `cancelled` and the generation still exists, the second call starts a new task with a new `id`; always track the `id` from the latest `202`. **How do I know the deletion finished?** Poll `GET /v1/deletions/{task_id}` until `state` is `completed` or `cancelled`; only `completed` means the content was removed. There is no deletion-completion webhook. Do not poll the generation itself — it can return partial content or `404` while the task is still `running`. **Are my uploaded files deleted too?** No. Files uploaded via `/v1/files` are independent of the generations that reference them by `file_id`; deleting a generation never deletes the file. Delete files you no longer need with `DELETE /v1/files/{file_id}` directly — see the [Files guide](/guides/files/index.md). **Is the billing history kept?** Yes. The credits charged for a deleted generation stay in your billing records; usage grouped by `tenant_id` or `metadata` reports them under `unattributed_credits`. **Why did I get a `404` when I can see the generation in the playground?** `"Not Found"` means the request did not reach a deletion: the path did not match a route (for example an empty id), or deletion is not enabled for your client; `"Generation not found"` means the id is not a UUID, is not yours, does not exist, or is not a root generation; your own generation already removed by a deletion task answers `202` with that task instead. **What does `overdue: true` mean?** The task passed its `delete_deadline` before completing, or completed after it — a `completed` task keeps `overdue: true`. `overdue` does not change `state`; a `blocked` task needs Luma to act whether or not it is overdue, so raise it with your support contact as soon as you see `blocked`. **Is this in the SDK?** No. The deletion routes are in the API Reference ([Delete a generation](/api/resources/generations/methods/delete/index.md), [Get a deletion](/api/resources/deletions/methods/get/index.md)) but not yet in the SDKs. Call them over HTTP as shown on this page.