---
title: "delete_job"
method: DELETE
path: "/v1/jobs/{job_id}"
tags: ["operations"]
---

# delete_job

`DELETE /v1/jobs/{job_id}`

## Request

- Base URL: `https://ndi-api.nace.ai`
- URL: `https://ndi-api.nace.ai/v1/jobs/{job_id}`
- Auth: API key in header `X-API-Key`
- TypeScript: <https://skmtc.dev/nace/apis/ndi-platform-api/docs/v1/jobs/:job_id/delete.ts> (a function with typed inputs; its response is checked against the schema)

Hide a job from listings and delete its content, keeping it for billing. Cost class: fast.

The row survives with its content-free billing fields, so ``units`` keep
answering and ``GET /v1/usage`` still counts the work; ``GET /v1/jobs`` stops
returning it. Reading it by id still works, with ``deleted_at`` set — the
same reasoning as an expired payload, where a 404 would conflate "gone" with
"never existed".

The job's ``input``, ``result``, artifact files and source snapshot are
removed by the reaper's content-deletion leg on its next sweep (minutes), with
the same for an automatic search's delegated child: the delete only marks
the payload as expired now, so slow storage work stays out of the request.
Afterwards the job reads as ``result_state="expired"`` (a zero-retention job
keeps ``not_retained``). The same sweep deletes the perception-cache
entries this job produced or consumed; one that another retained job of
the same tenant still reads stops serving hits and goes once that job's
content is deleted.
``input`` and ``result`` always go at that sweep. Only artifact files that
another retained job of the same tenant still uses (it reused this job's
Parse result, whose URLs point at them) are kept until that job's content
is deleted; a later sweep deletes them. A job still queued or running that
reuses this job's Parse result fails ``result_expired``. Bring-your-own-bucket
files are never deleted.

A job still queued or running is **cancelled first**, so hitting delete
stops the compute rather than paying for a run nobody will ever see. A job
that finished under us is simply hidden: there was nothing left to stop, and
refusing the delete over a race the caller cannot see would be noise.

``Temporal`` is not declared as a dependency here because asking for the
client connects: most deletes are of finished runs and need it for nothing,
so an outage must not fail work that is entirely in Postgres.

There is no project-level route to pair with ``?project_id=``: a run's
fan-out is one job per file, and the console deletes the group the same way
it renames it — per job, over the ids it already holds.

## Path parameters

- `job_id` string, uuid, required: Job identifier.

## Response `204`

Successful Response

## Other responses

- `504`: Gateway Timeout
- `default`: Typed error envelope used by every 4xx and 5xx. ``error.code`` distinguishes the failure; HTTP status is a consequence of the code.

---

[API](https://skmtc.dev/nace/apis/ndi-platform-api.md) - [All operations](https://skmtc.dev/nace/apis/ndi-platform-api/llms.txt) - [OpenAPI document](https://skmtc.dev/nace/apis/ndi-platform-api/revisions/54dad7e0ff00?raw)
