# impartire API — Reference: Asset comments and markers

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/{id}/comments

List an asset's review comments

Every comment on the asset (any of its versions — comments belong to the asset), flat: thread them by `parent_id`. In conversation order (`created_at`) unless `sort` says otherwise. `resolved=true|false` keeps only resolved or open comments (a reply on its own state). To poll, pass `updated_after` with the newest `updated_at` you have seen (compared to the millisecond, so that comment does not come back) and `sort=updated_at`; editing, resolving or reopening a comment moves its `updated_at`, and a deleted comment simply leaves the list. Written with POST (and PATCH / DELETE one) under `review:write`. 404 for an asset the credential cannot read (for an OAuth token, read on the asset).

Scopes: `review: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).
- `resolved` (query, "true" | "false") — `true`: only resolved comments; `false`: only open ones. Omitted: both. A reply is filtered on its own state, not its thread's.
- `updated_after` (query, string (date-time)) — Only comments changed strictly after this instant (ISO 8601 with offset), compared to the millisecond — pass the newest `updated_at` you have seen and that comment is not returned again.
- `sort` (query, "created_at" | "-created_at" | "updated_at" | "-updated_at") — Sort key; a leading `-` is descending. Default `created_at` (conversation order, as the app shows them). 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 — `CommentList`

- `data` (array of Comment, required)
  - `id` (string (uuid), required)
  - `body` (string, required)
  - `author` (object or null, required) — Who wrote it: `id` is their principal in the org, `name` their display name. Null if the author's account is gone. A comment written with an org API key is authored by the org's API service principal, named `API`.
    - `id` (string (uuid), required)
    - `name` (string, required)
  - `timestamp_seconds` (number or null, required) — The point in the media the comment is pinned to, in seconds from the start; null for a comment on the asset as a whole.
  - `parent_id` (string (uuid) or null, required) — The comment this one replies to, or null for a top-level comment. The list is flat: thread it by `parent_id`.
  - `resolved_at` (string (date-time) or null, required) — When it was marked resolved, or null while open.
  - `created_at` (string (date-time), required)
  - `updated_at` (string (date-time), required) — Moves when the body is edited or the comment is resolved or reopened.
- `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 `resolved`, `updated_after`, `sort`, `limit` or `cursor`.
- 429 (Problem) — rate-limited — the key's request budget is spent; wait Retry-After seconds.
- 500 (Problem) — internal.

#### POST /api/v1/assets/{id}/comments

Comment on an asset, or reply to a comment

Adds a review comment as the credential: an OAuth token writes as its person, who needs read on the asset (anyone who can see an asset may comment, as in the app); an API key writes as the org's API service principal, shown as `API`. `timestamp_seconds` pins it to a point in the media; `parent_id` makes it a reply to a comment on the same asset. Audited (the `comment` entries of GET /audit-log). Not a guarded action.

Scopes: `review: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).
- `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 — `CreateCommentRequest`

- `body` (string, required) — The comment's text. Trimmed; blank is refused.
- `timestamp_seconds` (number or null) — Pin it to this point in the media, in seconds from the start; omitted or null for a comment on the asset as a whole.
- `parent_id` (string (uuid) or null) — Reply to this comment (one on the same asset, from GET /api/v1/assets/{id}/comments); omitted or null for a top-level comment.

Response 201 — `Comment`: A review comment on an asset. Comments belong to the asset, not a version: every version shows the same ones.

- `id` (string (uuid), required)
- `body` (string, required)
- `author` (object or null, required) — Who wrote it: `id` is their principal in the org, `name` their display name. Null if the author's account is gone. A comment written with an org API key is authored by the org's API service principal, named `API`.
  - `id` (string (uuid), required)
  - `name` (string, required)
- `timestamp_seconds` (number or null, required) — The point in the media the comment is pinned to, in seconds from the start; null for a comment on the asset as a whole.
- `parent_id` (string (uuid) or null, required) — The comment this one replies to, or null for a top-level comment. The list is flat: thread it by `parent_id`.
- `resolved_at` (string (date-time) or null, required) — When it was marked resolved, or null while open.
- `created_at` (string (date-time), required)
- `updated_at` (string (date-time), required) — Moves when the body is edited or the comment is resolved or reopened.

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 (`body` blank or too long, `timestamp_seconds`, `parent_id` not a comment on this asset), or idempotency-mismatch.
- 429 (Problem) — rate-limited — the key's request budget is spent; wait Retry-After seconds.
- 500 (Problem) — internal.

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

Resolve or reopen a comment, or change its text

`resolved: true` marks it resolved, `false` reopens it; `body` replaces its text. For an OAuth token, its person may resolve, reopen (and delete) their own comments and any on an asset they may edit, and change the text of their own only; an API key may resolve or reopen any comment of its org and change the text only of comments written with the org's keys. Moves `updated_at`.

Scopes: `review: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).
- `comment_id` (path, string (uuid), required) — The comment's id, from GET /api/v1/assets/{id}/comments.

Request body — `UpdateCommentRequest`: At least one of `resolved` and `body`.

- `resolved` (boolean) — true marks it resolved (now), false reopens it. Setting the state it already has still moves updated_at.
- `body` (string) — New text — the author's alone (for a key: comments the key's org wrote through the API).

Response 200 — `Comment`: A review comment on an asset. Comments belong to the asset, not a version: every version shows the same ones.

- `id` (string (uuid), required)
- `body` (string, required)
- `author` (object or null, required) — Who wrote it: `id` is their principal in the org, `name` their display name. Null if the author's account is gone. A comment written with an org API key is authored by the org's API service principal, named `API`.
  - `id` (string (uuid), required)
  - `name` (string, required)
- `timestamp_seconds` (number or null, required) — The point in the media the comment is pinned to, in seconds from the start; null for a comment on the asset as a whole.
- `parent_id` (string (uuid) or null, required) — The comment this one replies to, or null for a top-level comment. The list is flat: thread it by `parent_id`.
- `resolved_at` (string (date-time) or null, required) — When it was marked resolved, or null while open.
- `created_at` (string (date-time), required)
- `updated_at` (string (date-time), required) — Moves when the body is edited or the comment is resolved or reopened.

Other responses:

- 401 (Problem) — unauthenticated — no key, or an unknown, revoked or expired one.
- 403 (Problem) — insufficient-scope, or insufficient-permission — not the author (for the text), or neither the author nor an editor of the asset.
- 404 (Problem) — not-found.
- 422 (Problem) — validation — neither `resolved` nor `body`, or a blank `body`.
- 429 (Problem) — rate-limited — the key's request budget is spent; wait Retry-After seconds.
- 500 (Problem) — internal.

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

Delete a comment

Hard-deletes it, as the app does. A comment with replies cannot be deleted (409 `conflict`): delete the replies first, or resolve it. A second DELETE is a 404. For an OAuth token, its person's own comments and any on an asset they may edit; an API key, any comment of its org.

Scopes: `review: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).
- `comment_id` (path, string (uuid), required) — The comment's id, from GET /api/v1/assets/{id}/comments.

Other responses:

- 204 — Deleted.
- 401 (Problem) — unauthenticated — no key, or an unknown, revoked or expired one.
- 403 (Problem) — insufficient-scope, or insufficient-permission — neither the author nor an editor of the asset.
- 404 (Problem) — not-found.
- 409 (Problem) — conflict — the comment has replies.
- 429 (Problem) — rate-limited — the key's request budget is spent; wait Retry-After seconds.
- 500 (Problem) — internal.

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

List an asset's clip markers

The asset's clip markers (labelled time ranges; any of its versions), in timeline order unless `sort` says otherwise. Returned whole: the app allows at most 3 per asset. `updated_after` (compared to the millisecond) keeps only markers made after it — markers are not edited, only made and deleted (POST and DELETE under `review:write`). 404 for an asset the credential cannot read (for an OAuth token, read on the asset).

Scopes: `review: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).
- `updated_after` (query, string (date-time)) — Only markers changed strictly after this instant (ISO 8601 with offset), compared to the millisecond.
- `sort` (query, "start_seconds" | "updated_at" | "-updated_at") — Default `start_seconds` (timeline order, as the app shows them); or `updated_at` / `-updated_at`. Ties break on id.

Response 200 — `MarkerList`

- `data` (array of Marker, required) — Returned whole: the app allows at most 3 markers per asset.
  - `id` (string (uuid), required)
  - `label` (string or null, required)
  - `start_seconds` (number, required) — Where the range starts, in seconds from the start of the media.
  - `end_seconds` (number, required) — Where it ends; always greater than start_seconds.
  - `created_by` (object or null, required) — Who marked it: `id` is their principal in the org, `name` their display name. Null if their account is gone. A marker made with an org API key names the org's API service principal, `API`.
    - `id` (string (uuid), required)
    - `name` (string, required)
  - `created_at` (string (date-time), required)
  - `updated_at` (string (date-time), required) — A marker is never edited (made or deleted, in the app or the API), so this equals created_at.
- `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.
- 422 (Problem) — validation — a bad `updated_after` or `sort`.
- 429 (Problem) — rate-limited — the key's request budget is spent; wait Retry-After seconds.
- 500 (Problem) — internal.

#### POST /api/v1/assets/{id}/markers

Mark a time range on an asset

Adds a clip marker as the credential (an OAuth token's person needs read on the asset; an API key marks as the org's API service principal, `API`). At most 3 per asset, as in the app: one more is 409 `conflict` — delete one first. Audited (the `marker` entries of GET /audit-log).

Scopes: `review: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).
- `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 — `CreateMarkerRequest`

- `start_seconds` (number, required) — Where the range starts, in seconds from the start of the media.
- `end_seconds` (number, required) — Where it ends; must be greater than start_seconds.
- `label` (string or null) — A short label; omitted or null for none.

Response 201 — `Marker`: A clip marker: a labelled time range on an asset (the app's clips). Markers belong to the asset, not a version.

- `id` (string (uuid), required)
- `label` (string or null, required)
- `start_seconds` (number, required) — Where the range starts, in seconds from the start of the media.
- `end_seconds` (number, required) — Where it ends; always greater than start_seconds.
- `created_by` (object or null, required) — Who marked it: `id` is their principal in the org, `name` their display name. Null if their account is gone. A marker made with an org API key names the org's API service principal, `API`.
  - `id` (string (uuid), required)
  - `name` (string, required)
- `created_at` (string (date-time), required)
- `updated_at` (string (date-time), required) — A marker is never edited (made or deleted, in the app or the API), so this equals created_at.

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) — conflict — the asset already has 3 markers; or idempotency-in-flight.
- 422 (Problem) — validation (`end_seconds` not after `start_seconds`, a negative time, a label over 200 characters), or idempotency-mismatch.
- 429 (Problem) — rate-limited — the key's request budget is spent; wait Retry-After seconds.
- 500 (Problem) — internal.

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

Delete a clip marker

Hard-deletes it, as the app does. A second DELETE is a 404. For an OAuth token, its person's own markers and any on an asset they may edit; an API key, any marker of its org.

Scopes: `review: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).
- `marker_id` (path, string (uuid), required) — The marker's id, from GET /api/v1/assets/{id}/markers.

Other responses:

- 204 — Deleted.
- 401 (Problem) — unauthenticated — no key, or an unknown, revoked or expired one.
- 403 (Problem) — insufficient-scope, or insufficient-permission — neither its creator nor an editor of the asset.
- 404 (Problem) — not-found.
- 429 (Problem) — rate-limited — the key's request budget is spent; wait Retry-After seconds.
- 500 (Problem) — internal.
