# impartire API — Reference: Assets

Every path is under `https://impartire.com/api/v1`. The machine-readable contract is https://impartire.com/api/v1/openapi.json (OpenAPI 3.1).

#### GET /api/v1/assets

List and search the org's assets

Every current asset in the org, whatever project it is in. Filters combine with AND. Keyset-paginated; no total. To poll for changes, pass `updated_after` with the newest `updated_at` you have seen and `sort=-updated_at` (or `updated_at`).

Scopes: `assets:read`

Parameters:

- `q` (query, string) — Case-insensitive substring of the asset's name. Characters are matched literally.
- `type_id` (query, string (uuid)) — Only assets of this type (GET /api/v1/asset-types).
- `project_id` (query, string (uuid)) — Only assets placed in this project (GET /api/v1/projects). Not its sub-projects.
- `external_ref` (query, string) — Only assets carrying exactly this external_ref.
- `created_after` (query, string (date-time)) — Only assets created strictly after this instant (ISO 8601 with offset).
- `created_before` (query, string (date-time)) — Only assets created strictly before this instant (ISO 8601 with offset).
- `updated_after` (query, string (date-time)) — Only assets changed strictly after this instant (ISO 8601 with offset), compared to the millisecond — poll with the newest `updated_at` you have seen (that asset does not come back), sorted `-updated_at` or `updated_at`.
- `category_id` (query, string (uuid)) — Only assets assigned this org category directly (a category an asset inherits from its project does not count).
- `media_type` (query, "video" | "audio" | "image" | "document" | "other") — Only assets with a current file of this media type in one of their slots (derived files do not count). `other` is any slotted file that is not video, audio, image or document — subtitles, for one.
- `tag` (query, array of string) — Only assets carrying a tag of this name — exact, ignoring case and extra spaces. Repeat the parameter (up to 10 times) for assets carrying every one.
- `sort` (query, "created_at" | "-created_at" | "name" | "-name" | "updated_at" | "-updated_at") — Sort key; a leading `-` is descending. Default `-created_at` (newest first). Ties break on id.
- `limit` (query, integer) — Page size, default 50. Values above 100 are treated as 100.
- `cursor` (query, string) — next_cursor from the previous page, with the other parameters unchanged.

Response 200 — `AssetSummaryList`

- `data` (array of AssetSummary, required)
  - `id` (string (uuid), required)
  - `name` (string, required)
  - `type` (object, required)
    - `id` (string (uuid), required)
    - `name` (string, required)
  - `project` (ProjectRef, required)
    - `id` (string (uuid), required)
    - `name` (string, required)
  - `external_ref` (string or null, required) — The caller's own id for this asset, as given at creation.
  - `lifecycle_phase` ("draft" | "ingested" | "verified" | "delivered", required)
  - `edit_seq` (integer, required) — Increments on every change to name, fields, type or project.
  - `created_at` (string (date-time), required)
  - `updated_at` (string (date-time), required)
- `next_cursor` (string or null, required) — Pass as `cursor` to get the next page; null means this is the last page.

Other responses:

- 401 (Problem) — unauthenticated — no key, or an unknown, revoked or expired one.
- 403 (Problem) — insufficient-scope — the key lacks a required scope.
- 404 (Problem) — not-found.
- 422 (Problem) — validation — a bad filter (`type_id`, `project_id`, `category_id`, `media_type`, `tag`, a timestamp), `sort`, `limit` or `cursor` (a cursor replayed with different parameters).
- 429 (Problem) — rate-limited — the key's request budget is spent; wait Retry-After seconds.
- 500 (Problem) — internal.

#### POST /api/v1/assets

Create an asset in a project, optionally from a landed upload

With `file`, the upload becomes the asset's first file and the slot's pipeline starts exactly as for a browser upload; `runs` shows what had started by the time of the response.

Scopes: `assets:write`

Parameters:

- `Idempotency-Key` (header, string) — Makes a retry safe: the same key and request within 24 hours replays the first response. 1–255 characters.

Request body — `CreateAssetRequest`

- `type_id` (string (uuid), required) — An asset type from GET /api/v1/asset-types.
- `project_id` (string (uuid), required) — The project the asset is placed in, from GET /api/v1/projects. Required: an API caller always names its project.
- `name` (string, required)
- `fields` (object (any keys)) — Field values keyed by field key, validated against the asset type.
- `external_ref` (string) — Your own id for this asset; returned on every read.
- `file` (object) — Attach an uploaded file. Its slot's pipeline (proxy, captions, QC…) starts as for a browser upload.
  - `upload_id` (string (uuid), required) — A landed upload this key created with POST /api/v1/uploads.
  - `slot_id` (string (uuid)) — The file slot (GET /api/v1/asset-types). Omit to use the type's one slot that matches the file's media type.

Response 201 — `Asset`

- `id` (string (uuid), required)
- `name` (string, required)
- `type` (object, required)
  - `id` (string (uuid), required)
  - `name` (string, required)
- `project` (ProjectRef, required)
  - `id` (string (uuid), required)
  - `name` (string, required)
- `fields` (object (any keys), required) — Field values keyed by field key, as defined by the asset type.
- `external_ref` (string or null, required) — The caller's own id for this asset, as given at creation.
- `lifecycle_phase` ("draft" | "ingested" | "verified" | "delivered", required)
- `edit_seq` (integer, required) — Increments on every change to name, fields, type or project. Also the ETag.
- `created_at` (string (date-time), required)
- `updated_at` (string (date-time), required)
- `files` (array of AssetFile, required)
  - `id` (string (uuid), required)
  - `slot` (object or null, required) — The slot a caller's file was attached to; null for a file the pipeline derived.
    - `id` (string (uuid), required)
    - `name` (string or null, required)
  - `role` (string or null, required) — null for a file attached to a slot; for a derived file, what the pipeline made: proxy, thumbnail, captions, conformed (more may be added).
  - `media_type` (string, required)
  - `mime_type` (string or null, required)
  - `extension` (string or null, required)
  - `size_bytes` (integer or null, required)
  - `version` (integer, required)
  - `uploaded_at` (string (date-time), required)
  - `root_file_id` (string (uuid), required) — The file's version chain: the id of its first version. Every version of the file shares it; GET /assets/{id}/files/{file_id} lists the chain.
  - `version_note` (string or null, required) — Why this version was added, as its uploader wrote it; null when none was given.
  - `uploaded_by` (object or null, required) — The person who added this version ({ id: their principal id, name }). Null for a file the pipeline derived and for one added with an API key (every key of an org acts as one service principal, so the key is not recoverable from the file).
    - `id` (string (uuid), required)
    - `name` (string, required)
- `runs` (array of Run, required) — The newest runs started against this asset, newest first, at most 10.
  - `id` (string (uuid), required)
  - `workflow` (object, required) — The template version the run is pinned to (any version's id names its template in GET /api/v1/workflows/{id}). `name` is null when the run is `private`.
    - `id` (string (uuid), required)
    - `name` (string or null, required)
  - `asset` (object, required) — The asset the run is about. `name` is null for an OAuth token whose person can no longer read the asset (a run they started).
    - `id` (string (uuid), required)
    - `name` (string or null, required)
  - `status` ("active" | "completed" | "cancelled" | "failed", required)
  - `private` (boolean, required) — True when the run's template is private to a team the token's person is not in: the run's existence, status, team and times are shown, its workflow name, steps, current step, incident and params are not (always false for an API key).
  - `team` (object or null, required) — The team that owns the run, snapshotted from its template at start; null for an unowned run.
    - `id` (string (uuid), required)
    - `name` (string, required)
  - `parent_run_id` (string (uuid) or null, required) — For a sub-workflow run, the run that started it.
  - `started_at` (string (date-time), required)
  - `updated_at` (string (date-time), required) — Moves on every change of state — poll on it with GET /api/v1/runs?updated_after=.
  - `completed_at` (string (date-time) or null, required)
  - `started_via` (string, required) — api (started through this API) | manual (in the app) | trigger (by an arrival trigger, e.g. an upload). More may be added.
  - `current_step` (object or null, required) — The first open step at the run's current gate, while the run is active; null otherwise.
    - `id` (string (uuid), required)
    - `name` (string, required)
    - `status` (string, required)
    - `awaiting` ("approval" | "completion" | "automation" or null, required) — What the step waits on: `approval` (a person's verdict), `completion` (a person marking it done), `automation` (the pipeline — an automated step, a gate or a sub-workflow); null while it is parked on an incident.
  - `steps` (array of RunStep, required)
    - `id` (string (uuid), required) — The template step's id (of the template version the run is pinned to).
    - `name` (string, required)
    - `status` (string, required) — pending | in_progress | submitted | approved | completed | failed | skipped | incident (more may be added).
  - `incident` (object or null, required) — Set while an active run is parked on a step that failed and waits for an operator to retry it or fail the run (in the app).
    - `message` (string, required)
  - `params` (object (any keys) or null, required) — The run's launch parameters, as validated against the template's param_schema.

Other responses:

- 401 (Problem) — unauthenticated — no key, or an unknown, revoked or expired one.
- 403 (Problem) — insufficient-scope — the key lacks a required scope.
- 404 (Problem) — not-found.
- 409 (Problem) — idempotency-in-flight.
- 422 (Problem) — validation (type_id, project_id, fields, file), upload-not-landed, or idempotency-mismatch.
- 429 (Problem) — rate-limited — the key's request budget is spent; wait Retry-After seconds.
- 500 (Problem) — internal.
- 503 (Problem) — storage-unavailable.

#### GET /api/v1/assets/{id}

Get an asset with its current files and recent runs

Scopes: `assets:read`

Parameters:

- `id` (path, string (uuid), required) — The asset's id: its current version's id or its root id (an older version's id is not found).
- `If-None-Match` (header, string) — An ETag from a previous response; 304 if unchanged.

Response 200 — `Asset`

- `id` (string (uuid), required)
- `name` (string, required)
- `type` (object, required)
  - `id` (string (uuid), required)
  - `name` (string, required)
- `project` (ProjectRef, required)
  - `id` (string (uuid), required)
  - `name` (string, required)
- `fields` (object (any keys), required) — Field values keyed by field key, as defined by the asset type.
- `external_ref` (string or null, required) — The caller's own id for this asset, as given at creation.
- `lifecycle_phase` ("draft" | "ingested" | "verified" | "delivered", required)
- `edit_seq` (integer, required) — Increments on every change to name, fields, type or project. Also the ETag.
- `created_at` (string (date-time), required)
- `updated_at` (string (date-time), required)
- `files` (array of AssetFile, required)
  - `id` (string (uuid), required)
  - `slot` (object or null, required) — The slot a caller's file was attached to; null for a file the pipeline derived.
    - `id` (string (uuid), required)
    - `name` (string or null, required)
  - `role` (string or null, required) — null for a file attached to a slot; for a derived file, what the pipeline made: proxy, thumbnail, captions, conformed (more may be added).
  - `media_type` (string, required)
  - `mime_type` (string or null, required)
  - `extension` (string or null, required)
  - `size_bytes` (integer or null, required)
  - `version` (integer, required)
  - `uploaded_at` (string (date-time), required)
  - `root_file_id` (string (uuid), required) — The file's version chain: the id of its first version. Every version of the file shares it; GET /assets/{id}/files/{file_id} lists the chain.
  - `version_note` (string or null, required) — Why this version was added, as its uploader wrote it; null when none was given.
  - `uploaded_by` (object or null, required) — The person who added this version ({ id: their principal id, name }). Null for a file the pipeline derived and for one added with an API key (every key of an org acts as one service principal, so the key is not recoverable from the file).
    - `id` (string (uuid), required)
    - `name` (string, required)
- `runs` (array of Run, required) — The newest runs started against this asset, newest first, at most 10.
  - `id` (string (uuid), required)
  - `workflow` (object, required) — The template version the run is pinned to (any version's id names its template in GET /api/v1/workflows/{id}). `name` is null when the run is `private`.
    - `id` (string (uuid), required)
    - `name` (string or null, required)
  - `asset` (object, required) — The asset the run is about. `name` is null for an OAuth token whose person can no longer read the asset (a run they started).
    - `id` (string (uuid), required)
    - `name` (string or null, required)
  - `status` ("active" | "completed" | "cancelled" | "failed", required)
  - `private` (boolean, required) — True when the run's template is private to a team the token's person is not in: the run's existence, status, team and times are shown, its workflow name, steps, current step, incident and params are not (always false for an API key).
  - `team` (object or null, required) — The team that owns the run, snapshotted from its template at start; null for an unowned run.
    - `id` (string (uuid), required)
    - `name` (string, required)
  - `parent_run_id` (string (uuid) or null, required) — For a sub-workflow run, the run that started it.
  - `started_at` (string (date-time), required)
  - `updated_at` (string (date-time), required) — Moves on every change of state — poll on it with GET /api/v1/runs?updated_after=.
  - `completed_at` (string (date-time) or null, required)
  - `started_via` (string, required) — api (started through this API) | manual (in the app) | trigger (by an arrival trigger, e.g. an upload). More may be added.
  - `current_step` (object or null, required) — The first open step at the run's current gate, while the run is active; null otherwise.
    - `id` (string (uuid), required)
    - `name` (string, required)
    - `status` (string, required)
    - `awaiting` ("approval" | "completion" | "automation" or null, required) — What the step waits on: `approval` (a person's verdict), `completion` (a person marking it done), `automation` (the pipeline — an automated step, a gate or a sub-workflow); null while it is parked on an incident.
  - `steps` (array of RunStep, required)
    - `id` (string (uuid), required) — The template step's id (of the template version the run is pinned to).
    - `name` (string, required)
    - `status` (string, required) — pending | in_progress | submitted | approved | completed | failed | skipped | incident (more may be added).
  - `incident` (object or null, required) — Set while an active run is parked on a step that failed and waits for an operator to retry it or fail the run (in the app).
    - `message` (string, required)
  - `params` (object (any keys) or null, required) — The run's launch parameters, as validated against the template's param_schema.

Other responses:

- 304 — Not modified — the ETag in If-None-Match is current.
- 401 (Problem) — unauthenticated — no key, or an unknown, revoked or expired one.
- 403 (Problem) — insufficient-scope — the key lacks a required scope.
- 404 (Problem) — not-found.
- 429 (Problem) — rate-limited — the key's request budget is spent; wait Retry-After seconds.
- 500 (Problem) — internal.

#### PATCH /api/v1/assets/{id}

Update an asset's name, fields, project or external_ref

If-Match is required: send the ETag from the last GET (or the bare `edit_seq`). `fields` replaces the whole bag. A `project_id` change moves the asset; filing it into a project that is not open revokes its share links.

Scopes: `assets:write`

Parameters:

- `id` (path, string (uuid), required) — The asset's id: its current version's id or its root id (an older version's id is not found).
- `If-Match` (header, string, required) — The asset's ETag, or its bare edit_seq.

Request body — `UpdateAssetRequest`: At least one of name, fields, project_id, external_ref.

- `name` (string)
- `fields` (object (any keys)) — The asset's whole field bag, validated against its type: keys left out are removed (send back what GET returned, changed). Legacy `<key>__legacy` values are carried forward unless sent as null.
- `project_id` (string (uuid)) — Move the asset to this project (GET /api/v1/projects).
- `external_ref` (string or null) — Your own id for this asset; null clears it.
- `schema_version` (integer) — The asset type's schema_version your `fields` were built against. When they fail validation and the type has moved past it, the answer is 409 type-changed instead of 422.

Response 200 — `Asset`

- `id` (string (uuid), required)
- `name` (string, required)
- `type` (object, required)
  - `id` (string (uuid), required)
  - `name` (string, required)
- `project` (ProjectRef, required)
  - `id` (string (uuid), required)
  - `name` (string, required)
- `fields` (object (any keys), required) — Field values keyed by field key, as defined by the asset type.
- `external_ref` (string or null, required) — The caller's own id for this asset, as given at creation.
- `lifecycle_phase` ("draft" | "ingested" | "verified" | "delivered", required)
- `edit_seq` (integer, required) — Increments on every change to name, fields, type or project. Also the ETag.
- `created_at` (string (date-time), required)
- `updated_at` (string (date-time), required)
- `files` (array of AssetFile, required)
  - `id` (string (uuid), required)
  - `slot` (object or null, required) — The slot a caller's file was attached to; null for a file the pipeline derived.
    - `id` (string (uuid), required)
    - `name` (string or null, required)
  - `role` (string or null, required) — null for a file attached to a slot; for a derived file, what the pipeline made: proxy, thumbnail, captions, conformed (more may be added).
  - `media_type` (string, required)
  - `mime_type` (string or null, required)
  - `extension` (string or null, required)
  - `size_bytes` (integer or null, required)
  - `version` (integer, required)
  - `uploaded_at` (string (date-time), required)
  - `root_file_id` (string (uuid), required) — The file's version chain: the id of its first version. Every version of the file shares it; GET /assets/{id}/files/{file_id} lists the chain.
  - `version_note` (string or null, required) — Why this version was added, as its uploader wrote it; null when none was given.
  - `uploaded_by` (object or null, required) — The person who added this version ({ id: their principal id, name }). Null for a file the pipeline derived and for one added with an API key (every key of an org acts as one service principal, so the key is not recoverable from the file).
    - `id` (string (uuid), required)
    - `name` (string, required)
- `runs` (array of Run, required) — The newest runs started against this asset, newest first, at most 10.
  - `id` (string (uuid), required)
  - `workflow` (object, required) — The template version the run is pinned to (any version's id names its template in GET /api/v1/workflows/{id}). `name` is null when the run is `private`.
    - `id` (string (uuid), required)
    - `name` (string or null, required)
  - `asset` (object, required) — The asset the run is about. `name` is null for an OAuth token whose person can no longer read the asset (a run they started).
    - `id` (string (uuid), required)
    - `name` (string or null, required)
  - `status` ("active" | "completed" | "cancelled" | "failed", required)
  - `private` (boolean, required) — True when the run's template is private to a team the token's person is not in: the run's existence, status, team and times are shown, its workflow name, steps, current step, incident and params are not (always false for an API key).
  - `team` (object or null, required) — The team that owns the run, snapshotted from its template at start; null for an unowned run.
    - `id` (string (uuid), required)
    - `name` (string, required)
  - `parent_run_id` (string (uuid) or null, required) — For a sub-workflow run, the run that started it.
  - `started_at` (string (date-time), required)
  - `updated_at` (string (date-time), required) — Moves on every change of state — poll on it with GET /api/v1/runs?updated_after=.
  - `completed_at` (string (date-time) or null, required)
  - `started_via` (string, required) — api (started through this API) | manual (in the app) | trigger (by an arrival trigger, e.g. an upload). More may be added.
  - `current_step` (object or null, required) — The first open step at the run's current gate, while the run is active; null otherwise.
    - `id` (string (uuid), required)
    - `name` (string, required)
    - `status` (string, required)
    - `awaiting` ("approval" | "completion" | "automation" or null, required) — What the step waits on: `approval` (a person's verdict), `completion` (a person marking it done), `automation` (the pipeline — an automated step, a gate or a sub-workflow); null while it is parked on an incident.
  - `steps` (array of RunStep, required)
    - `id` (string (uuid), required) — The template step's id (of the template version the run is pinned to).
    - `name` (string, required)
    - `status` (string, required) — pending | in_progress | submitted | approved | completed | failed | skipped | incident (more may be added).
  - `incident` (object or null, required) — Set while an active run is parked on a step that failed and waits for an operator to retry it or fail the run (in the app).
    - `message` (string, required)
  - `params` (object (any keys) or null, required) — The run's launch parameters, as validated against the template's param_schema.

Other responses:

- 401 (Problem) — unauthenticated — no key, or an unknown, revoked or expired one.
- 403 (Problem) — insufficient-scope — the key lacks a required scope.
- 404 (Problem) — not-found.
- 409 (Problem) — type-changed — the type's schema moved past `schema_version` and the fields no longer validate.
- 412 (Problem) — precondition-failed — If-Match is stale; `edit_seq` in the body is the current one.
- 422 (Problem) — validation (empty body, name, fields, project_id, external_ref).
- 428 (Problem) — precondition-required — no If-Match.
- 429 (Problem) — rate-limited — the key's request budget is spent; wait Retry-After seconds.
- 500 (Problem) — internal.

#### DELETE /api/v1/assets/{id}

Delete an asset (every version)

Soft-deletes the asset; it no longer appears in reads or lists. No If-Match: deleting is idempotent by outcome, so a second DELETE is a 404. An asset under legal hold is refused with 409 `legal-hold`, whose detail gives the hold's reason.

Scopes: `assets:write`

Parameters:

- `id` (path, string (uuid), required) — The asset's id: its current version's id or its root id (an older version's id is not found).

Other responses:

- 204 — Deleted.
- 401 (Problem) — unauthenticated — no key, or an unknown, revoked or expired one.
- 403 (Problem) — insufficient-scope — the key lacks a required scope.
- 404 (Problem) — not-found.
- 409 (Problem) — legal-hold — the asset is under legal hold.
- 429 (Problem) — rate-limited — the key's request budget is spent; wait Retry-After seconds.
- 500 (Problem) — internal.

#### GET /api/v1/assets/{id}/referenced-by

List the records that reference an asset

Every live record whose reference field points at the asset (any of its versions), as `{ record, field_key, field_name }` — a record pointing at it through two fields appears twice — ordered by the record's title. For an OAuth token, records its person may not read and fields they may not read are left out, so a page may be short and still have a next one. 404 when the org does not have custom records, and for an asset the credential cannot read.

Scopes: `assets:read`

Parameters:

- `id` (path, string (uuid), required) — The asset's id: its current version's id or its root id (an older version's id is not found).
- `limit` (query, integer) — Page size, default 50. Values above 100 are treated as 100.
- `cursor` (query, string) — next_cursor from the previous page.

Response 200 — `ReferencingRecordList`

- `data` (array of ReferencingRecord, required)
  - `record` (object, required)
    - `id` (string (uuid), required)
    - `name` (string, required) — The record's title.
    - `type` (object, required)
      - `id` (string (uuid), required)
      - `name` (string, required)
  - `field_key` (string, required) — The record's reference field that points at the asset.
  - `field_name` (string, required) — That field's label, as the app shows it.
- `next_cursor` (string or null, required) — Pass as `cursor` to get the next page; null means this is the last page. A page may hold fewer than `limit` entries (those the credential may not see are left out) and still have a next page.

Other responses:

- 401 (Problem) — unauthenticated — no key, or an unknown, revoked or expired one.
- 403 (Problem) — insufficient-scope — the key lacks a required scope.
- 404 (Problem) — not-found.
- 422 (Problem) — validation — a bad `limit` or `cursor`.
- 429 (Problem) — rate-limited — the key's request budget is spent; wait Retry-After seconds.
- 500 (Problem) — internal.

#### GET /api/v1/assets/{id}/lineage

Get an asset's derivation lineage

What the asset derives from (`ancestors`) and what derives from it (`descendants`) — proxies, clips promoted from it, deliveries — over the lineage graph, one node per asset at its smallest depth, at most 64 steps each way, returned whole. Where an edge joins another organisation (a delivery to or from it) the walk stops and the node is a **boundary**: `asset: null`, `restricted: true`, `boundary: { org_name, role, receipt_id }` — nothing of the other org's asset or graph. For an OAuth token, an asset in the graph its person may not read keeps its place, depth and type with `asset: null` and `restricted: true`. 404 for an asset the credential cannot read.

Scopes: `assets:read`

Parameters:

- `id` (path, string (uuid), required) — The asset's id: its current version's id or its root id (an older version's id is not found).

Response 200 — `Lineage`: An asset's derivation lineage: ancestors and descendants over the lineage graph, at most 64 steps each way, cycle-guarded. Returned whole.

- `root_id` (string (uuid), required) — The asset's root id (its first version's id).
- `self` (LineageNode, required) — The asset asked about, at depth 0.
  - `asset` (object or null, required) — The asset (its current version's id and name). Null for a boundary node, and — for an OAuth token — for an asset its person may not read (`restricted`).
    - `id` (string (uuid), required)
    - `name` (string, required)
  - `version` (integer or null, required) — The asset's current version number; null for a boundary node.
  - `type` (object or null, required) — The asset's type — kept on a restricted node (the envelope); null for a boundary node or an untyped asset.
    - `id` (string (uuid), required)
    - `name` (string, required)
  - `depth` (integer, required) — Steps from the asset asked about: 0 for `self`, 1 for a direct parent or child.
  - `relationship` (string or null, required) — The edge's role (`derivation`, `delivered`…) by which the node was reached at its smallest depth; null for `self`.
  - `restricted` (boolean, required) — True when the node's content is withheld: a boundary node, or an asset an OAuth token's person may not read. The node keeps its place so the chain has no hole.
  - `boundary` (LineageBoundary or null, required) — Set when the edge here joins another org; the walk stops at it.
    - `org_name` (string or null, required) — The other organisation's name.
    - `role` (string, required) — The edge's relationship, e.g. `delivered`.
    - `receipt_id` (string (uuid) or null, required) — The delivery receipt that wrote the edge, when there is one.
- `ancestors` (array of LineageNode, required) — What it derives from, nearest first (one node per asset, at its smallest depth), then any boundary met.
  - `asset` (object or null, required) — The asset (its current version's id and name). Null for a boundary node, and — for an OAuth token — for an asset its person may not read (`restricted`).
    - `id` (string (uuid), required)
    - `name` (string, required)
  - `version` (integer or null, required) — The asset's current version number; null for a boundary node.
  - `type` (object or null, required) — The asset's type — kept on a restricted node (the envelope); null for a boundary node or an untyped asset.
    - `id` (string (uuid), required)
    - `name` (string, required)
  - `depth` (integer, required) — Steps from the asset asked about: 0 for `self`, 1 for a direct parent or child.
  - `relationship` (string or null, required) — The edge's role (`derivation`, `delivered`…) by which the node was reached at its smallest depth; null for `self`.
  - `restricted` (boolean, required) — True when the node's content is withheld: a boundary node, or an asset an OAuth token's person may not read. The node keeps its place so the chain has no hole.
  - `boundary` (LineageBoundary or null, required) — Set when the edge here joins another org; the walk stops at it.
    - `org_name` (string or null, required) — The other organisation's name.
    - `role` (string, required) — The edge's relationship, e.g. `delivered`.
    - `receipt_id` (string (uuid) or null, required) — The delivery receipt that wrote the edge, when there is one.
- `descendants` (array of LineageNode, required) — What derives from it, nearest first, likewise.
  - `asset` (object or null, required) — The asset (its current version's id and name). Null for a boundary node, and — for an OAuth token — for an asset its person may not read (`restricted`).
    - `id` (string (uuid), required)
    - `name` (string, required)
  - `version` (integer or null, required) — The asset's current version number; null for a boundary node.
  - `type` (object or null, required) — The asset's type — kept on a restricted node (the envelope); null for a boundary node or an untyped asset.
    - `id` (string (uuid), required)
    - `name` (string, required)
  - `depth` (integer, required) — Steps from the asset asked about: 0 for `self`, 1 for a direct parent or child.
  - `relationship` (string or null, required) — The edge's role (`derivation`, `delivered`…) by which the node was reached at its smallest depth; null for `self`.
  - `restricted` (boolean, required) — True when the node's content is withheld: a boundary node, or an asset an OAuth token's person may not read. The node keeps its place so the chain has no hole.
  - `boundary` (LineageBoundary or null, required) — Set when the edge here joins another org; the walk stops at it.
    - `org_name` (string or null, required) — The other organisation's name.
    - `role` (string, required) — The edge's relationship, e.g. `delivered`.
    - `receipt_id` (string (uuid) or null, required) — The delivery receipt that wrote the edge, when there is one.

Other responses:

- 401 (Problem) — unauthenticated — no key, or an unknown, revoked or expired one.
- 403 (Problem) — insufficient-scope — the key lacks a required scope.
- 404 (Problem) — not-found.
- 429 (Problem) — rate-limited — the key's request budget is spent; wait Retry-After seconds.
- 500 (Problem) — internal.

#### GET /api/v1/assets/{id}/playback

Get an asset's playback descriptor

What a player needs to play the asset's primary video: `status` (`ready`, `processing` or `none`), the HLS manifest URL (`hls_url`, carrying its signed access token when the delivery is signed), the poster, the WebVTT caption tracks and the progressive MP4 proxies. Every URL is short-lived: `expires_at` is the earliest moment one may stop working, so fetch the descriptor again rather than caching a URL. `processing` carries no URL; `none` means there is no HLS ladder to play (no video, a failed or missing transcode, or a delivery the API cannot play yet) — proxies, captions and poster are still listed where they exist. The original upload is never served here: playing is a read, the original is an export (`GET /assets/{id}/files/{file_id}/download`). 404 for an asset the credential cannot read (for an OAuth token, read on the asset).

Scopes: `assets:read`

Parameters:

- `id` (path, string (uuid), required) — The asset's id: its current version's id or its root id (an older version's id is not found).

Response 200 — `Playback`: What a player needs to play an asset's primary video: the HLS manifest, poster, caption tracks and proxies — every URL short-lived, never the original (the master is an export, not a read).

- `status` ("ready" | "processing" | "none", required) — `ready`: the HLS ladder is packaged and `hls_url` plays it. `processing`: the ladder is being made — nothing else in the descriptor is set; ask again. `none`: there is no ladder to play — the asset has no video, its transcode failed or was never started, or its delivery cannot be played through the API yet.
- `hls_url` (string or null, required) — The HLS manifest (with its signed access token when the delivery is signed); set only when `status` is `ready`. Do not store it: fetch the descriptor again for a fresh one.
- `expires_at` (string (date-time) or null, required) — The earliest moment a URL in this descriptor may stop working; null when it carries no URL. Re-fetch the descriptor before then rather than caching URLs.
- `poster_url` (string or null, required) — A presigned GET for the asset's poster still, when it has one.
- `duration_seconds` (number or null, required) — The played file's measured duration; null until measured or when there is no video.
- `captions` (array of PlaybackCaption, required) — Caption tracks, originals before machine translations, oldest first — the first is the default track.
  - `language` (string or null, required) — The track's BCP 47 language tag, or null when it is not recorded.
  - `label` (string, required) — A label for a track menu: the language's own name, plus " (auto-translated)" or " (auto-generated)" for a machine-made track; else the slot's name or `Subtitles`.
  - `url` (string, required) — A presigned GET for the WebVTT file. Stops working at the descriptor's `expires_at`.
  - `kind` ("subtitles", required) — The `<track kind>` to use. Always `subtitles` today.
- `proxies` (array of PlaybackProxy, required) — Progressive MP4 proxies derived from the played file, tallest first.
  - `variant` (string, required) — The rendition's variant, e.g. `1080p`, `720p`.
  - `url` (string, required) — A presigned GET for the MP4. Stops working at the descriptor's `expires_at`.
  - `width` (integer or null, required) — Pixels, as measured; null until measured.
  - `height` (integer or null, required) — Pixels, as measured; null until measured.

Other responses:

- 401 (Problem) — unauthenticated — no key, or an unknown, revoked or expired one.
- 403 (Problem) — insufficient-scope — the key lacks a required scope.
- 404 (Problem) — not-found.
- 429 (Problem) — rate-limited — the key's request budget is spent; wait Retry-After seconds.
- 500 (Problem) — internal.
- 503 (Problem) — storage-unavailable — the URLs or the delivery's access token could not be signed; retry.

#### GET /api/v1/assets/{id}/credits

List the people credited on an asset

The asset's credits (credits belong to the asset, not a version): `{ id, person, role, billing_order }`, by billing order (unset last), then name. Returned whole, at most 500. Archived people are left out. Read only: credits are given in the app. 404 for an asset the credential cannot read (for an OAuth token, read on the asset). 404 when the org does not have people.

Scopes: `assets:read`

Parameters:

- `id` (path, string (uuid), required) — The asset's id: its current version's id or its root id (an older version's id is not found).

Response 200 — `AssetCreditList`

- `data` (array of AssetCredit, required) — By billing_order (unset last), then the person's name. Returned whole, at most 500.
  - `id` (string (uuid), required) — The credit's id.
  - `person` (object, required)
    - `id` (string (uuid), required)
    - `name` (string, required)
  - `role` (string or null, required) — The role credited (`Director`, `Host`…), as entered in the app; null when none was given.
  - `billing_order` (integer or null, required) — Billing position, lowest first; null when unset.
- `next_cursor` (string or null, required) — Always null: the list is returned whole.

Other responses:

- 401 (Problem) — unauthenticated — no key, or an unknown, revoked or expired one.
- 403 (Problem) — insufficient-scope — the key lacks a required scope.
- 404 (Problem) — not-found.
- 429 (Problem) — rate-limited — the key's request budget is spent; wait Retry-After seconds.
- 500 (Problem) — internal.

#### GET /api/v1/assets/{id}/categories

List an asset's categories

The categories assigned to the asset's current version, each with its group, returned whole — the assignments `GET /assets?category_id=` matches on (a category the asset's project carries is not listed). Read only: categories are assigned in the app. 404 for an asset the credential cannot read (for an OAuth token, read on the asset). 404 when the org does not have categories.

Scopes: `assets:read`

Parameters:

- `id` (path, string (uuid), required) — The asset's id: its current version's id or its root id (an older version's id is not found).

Response 200 — `AssetCategoryList`

- `data` (array of AssetCategory, required) — By group name, then name. Returned whole.
  - `id` (string (uuid), required) — The category's id (GET /api/v1/categories).
  - `name` (string, required)
  - `group` (object, required)
    - `id` (string (uuid), required)
    - `name` (string, required)
- `next_cursor` (string or null, required) — Always null: the list is returned whole.

Other responses:

- 401 (Problem) — unauthenticated — no key, or an unknown, revoked or expired one.
- 403 (Problem) — insufficient-scope — the key lacks a required scope.
- 404 (Problem) — not-found.
- 429 (Problem) — rate-limited — the key's request budget is spent; wait Retry-After seconds.
- 500 (Problem) — internal.
