# impartire API — Reference: Asset share links

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}/share-links

List an asset's public share links

Newest first: whether each has a password, its expiry, clip and download setting, how often it was opened, who made it. Never a link's URL or token — those are returned once, when the link is made — and never its password. For an OAuth token, its person must be able to share the asset (asset share), as in the app (403 otherwise; 404 when they cannot open it).

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 — `ShareLinkList`: A page of an asset's share links, newest first.

- `data` (array of ShareLink, required)
  - `id` (string (uuid), required)
  - `asset` (object, required) — The asset it shares (its current version's id and name).
    - `id` (string (uuid), required)
    - `name` (string, required)
  - `has_password` (boolean, required)
  - `expires_at` (string (date-time) or null, required) — When it stops working; null: never.
  - `expired` (boolean, required)
  - `allow_download` (boolean, required)
  - `clip_start_seconds` (number or null, required)
  - `clip_end_seconds` (number or null, required)
  - `view_count` (integer, required) — How many times it was opened.
  - `created_by` (object or null, required) — Who made it: a member, or `API` for a link made with an org API key. Null if their account is gone.
    - `id` (string (uuid), required)
    - `name` (string, required)
  - `created_at` (string (date-time), required)
- `next_cursor` (string or null, required)

Other responses:

- 401 (Problem) — unauthenticated — no key, or an unknown, revoked or expired one.
- 403 (Problem) — insufficient-scope, or insufficient-permission — the token's person cannot share the asset.
- 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.

#### POST /api/v1/assets/{id}/share-links

Make a public share link to an asset (guarded)

A guarded action (ADR 0081): anyone holding the link can watch the asset without signing in. The app's own write runs: a password is stored salted and hashed (the viewer enters it on the link's page; it is never sent in a URL); a clip pins the link to a cut; an asset under legal hold is 409 `legal-hold`. **An API key's link is made at once**: 201 with the link and its `url` — the ONLY time the URL is returned; keep it as you would a password. **An OAuth token's is not**: 202 with a Confirmation naming the asset, the expiry, download and password; once its person confirms it in impartire the link is made as them, and the confirmation's `result` is the CreatedShareLink with its `url` (readable only by the app and person that asked). Its person must be able to share the asset (no platform-admin bypass). Audited: a `share_link.created` event and the row's capture.

Scopes: `sharing: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 — `CreateShareLinkRequest`

- `expires_in_days` (integer or null) — The link stops working this many days from now (1–365). Null or omitted: it never expires.
- `password` (string or null) — A password the viewer must enter. Stored salted and hashed, never readable again. Null or omitted: none.
- `allow_download` (boolean) — Whether the viewer may download the video. Default false.
- `clip_start_seconds` (number or null) — Pin the link to a cut: its start, in seconds. Set with clip_end_seconds.
- `clip_end_seconds` (number or null) — The cut's end, in seconds; after clip_start_seconds.

Response 201 — `CreatedShareLink`: A share link just created, with its URL — the only time the URL is returned.

- `id` (string (uuid), required)
- `asset` (object, required) — The asset it shares (its current version's id and name).
  - `id` (string (uuid), required)
  - `name` (string, required)
- `has_password` (boolean, required)
- `expires_at` (string (date-time) or null, required) — When it stops working; null: never.
- `expired` (boolean, required)
- `allow_download` (boolean, required)
- `clip_start_seconds` (number or null, required)
- `clip_end_seconds` (number or null, required)
- `view_count` (integer, required) — How many times it was opened.
- `created_by` (object or null, required) — Who made it: a member, or `API` for a link made with an org API key. Null if their account is gone.
  - `id` (string (uuid), required)
  - `name` (string, required)
- `created_at` (string (date-time), required)
- `url` (string, required) — The public URL — the token in it is the link's whole authority. Returned ONCE, here (and in the confirmation's `result` for an OAuth token); never listed again. Keep it as you would a password.

Response 202 — `Confirmation`: A guarded action an OAuth token asked for, waiting for its person to confirm it in impartire (ADR 0081). A guarded endpoint answers 202 with this and a Location; poll it until `status` is executed, declined or expired.

- `id` (string (uuid), required)
- `status` ("pending" | "confirmed" | "declined" | "expired" | "executed", required) — pending: waiting for the person to confirm or decline it in impartire (at `confirm_url`). confirmed: they confirmed and the action is running — poll again. executed: it ran; `result` holds its answer, or `problem` why it failed. declined: they said no; it will never run. expired: nobody decided within 15 minutes; it will never run (GET /api/v1/confirmations/{id} answers 410 confirmation-expired instead of this body; the person still sees it in impartire).
- `action` (string, required) — The guarded action, e.g. `run.signal`.
- `resource` (object, required)
  - `type` (string, required) — The kind of resource the action is on, e.g. `run`.
  - `id` (string (uuid) or null, required)
  - `name` (string, required) — The resource as the person was shown it when the action was asked for.
- `summary` (string, required) — The action in words, as the person reads it on the confirmation page.
- `client_id` (string, required) — The OAuth client that asked.
- `confirm_url` (string, required) — Where the person confirms or declines it. Show it to them; the API cannot confirm.
- `created_at` (string (date-time), required)
- `expires_at` (string (date-time), required) — 15 minutes after it was asked for. A pending confirmation past it is expired.
- `decided_at` (string (date-time) or null, required) — When the person confirmed or declined it, or when it expired; null while pending.
- `result` (any, required) — When executed and the action succeeded: the action's answer (what the endpoint would have returned with a key). Null otherwise.
- `problem` (Problem or null, required) — When executed and the action failed: the Problem it failed with. Null otherwise.
  - `type` (string, required) — A stable URL naming the problem: https://impartire.com/problems/<code>. Branch on this.
  - `title` (string, required)
  - `status` (integer, required)
  - `detail` (string, required) — What went wrong and what to do about it, written for the caller.
  - `instance` (string, required) — The request path.
  - `request_id` (string, required) — Also sent as the Request-Id header. Quote it when reporting a problem.
  - `errors` (array of object) — On 422 validation: one entry per invalid field.
    - `path` (string, required) — The field or parameter, as a dotted path.
    - `message` (string, required)
  - `edit_seq` (integer) — On 412 precondition-failed: the resource's current edit_seq.
  - `etag` (string) — On 412 precondition-failed for a project, a collection, a playlist or a schedule slot: its current ETag.
  - `item_id` (string) — On 409 already-member: the existing item's id (in the collection or playlist).
  - `schema_version` (integer) — On 409 type-changed: the asset type's current schema_version.
  - `confirmation_id` (string) — On 409 confirmation-pending: the confirmation already waiting for its person (GET /api/v1/confirmations/{id}).
  - `confirm_url` (string) — On 409 confirmation-pending: where the person confirms or declines it, in impartire.

Other responses:

- 401 (Problem) — unauthenticated — no key, or an unknown, revoked or expired one.
- 403 (Problem) — insufficient-scope, or insufficient-permission — the token's person cannot share the asset.
- 404 (Problem) — not-found.
- 409 (Problem) — legal-hold — the asset is held; confirmation-pending; or idempotency-in-flight.
- 422 (Problem) — validation (a bad field; a clip with one end, or an end not after its start), 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}/share-links/{link_id}

Revoke a public share link (guarded)

A guarded action (ADR 0081): the link stops working for everyone who has it, at once. For an OAuth token, the link's creator or a person who can share the asset, as in the app. **An API key's revoke happens at once** (204). **An OAuth token's does not**: 202 with a Confirmation; once its person confirms it in impartire the link is revoked as them. Audited: a `share_link.revoked` event and the row's capture.

Scopes: `sharing: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).
- `link_id` (path, string (uuid), required) — The link's id, from GET /api/v1/assets/{id}/share-links or its create.

Response 202 — `Confirmation`: A guarded action an OAuth token asked for, waiting for its person to confirm it in impartire (ADR 0081). A guarded endpoint answers 202 with this and a Location; poll it until `status` is executed, declined or expired.

- `id` (string (uuid), required)
- `status` ("pending" | "confirmed" | "declined" | "expired" | "executed", required) — pending: waiting for the person to confirm or decline it in impartire (at `confirm_url`). confirmed: they confirmed and the action is running — poll again. executed: it ran; `result` holds its answer, or `problem` why it failed. declined: they said no; it will never run. expired: nobody decided within 15 minutes; it will never run (GET /api/v1/confirmations/{id} answers 410 confirmation-expired instead of this body; the person still sees it in impartire).
- `action` (string, required) — The guarded action, e.g. `run.signal`.
- `resource` (object, required)
  - `type` (string, required) — The kind of resource the action is on, e.g. `run`.
  - `id` (string (uuid) or null, required)
  - `name` (string, required) — The resource as the person was shown it when the action was asked for.
- `summary` (string, required) — The action in words, as the person reads it on the confirmation page.
- `client_id` (string, required) — The OAuth client that asked.
- `confirm_url` (string, required) — Where the person confirms or declines it. Show it to them; the API cannot confirm.
- `created_at` (string (date-time), required)
- `expires_at` (string (date-time), required) — 15 minutes after it was asked for. A pending confirmation past it is expired.
- `decided_at` (string (date-time) or null, required) — When the person confirmed or declined it, or when it expired; null while pending.
- `result` (any, required) — When executed and the action succeeded: the action's answer (what the endpoint would have returned with a key). Null otherwise.
- `problem` (Problem or null, required) — When executed and the action failed: the Problem it failed with. Null otherwise.
  - `type` (string, required) — A stable URL naming the problem: https://impartire.com/problems/<code>. Branch on this.
  - `title` (string, required)
  - `status` (integer, required)
  - `detail` (string, required) — What went wrong and what to do about it, written for the caller.
  - `instance` (string, required) — The request path.
  - `request_id` (string, required) — Also sent as the Request-Id header. Quote it when reporting a problem.
  - `errors` (array of object) — On 422 validation: one entry per invalid field.
    - `path` (string, required) — The field or parameter, as a dotted path.
    - `message` (string, required)
  - `edit_seq` (integer) — On 412 precondition-failed: the resource's current edit_seq.
  - `etag` (string) — On 412 precondition-failed for a project, a collection, a playlist or a schedule slot: its current ETag.
  - `item_id` (string) — On 409 already-member: the existing item's id (in the collection or playlist).
  - `schema_version` (integer) — On 409 type-changed: the asset type's current schema_version.
  - `confirmation_id` (string) — On 409 confirmation-pending: the confirmation already waiting for its person (GET /api/v1/confirmations/{id}).
  - `confirm_url` (string) — On 409 confirmation-pending: where the person confirms or declines it, in impartire.

Other responses:

- 204 — An API key's revoke: the link is gone.
- 401 (Problem) — unauthenticated — no key, or an unknown, revoked or expired one.
- 403 (Problem) — insufficient-scope, or insufficient-permission — neither its creator nor someone who can share the asset.
- 404 (Problem) — not-found.
- 409 (Problem) — confirmation-pending.
- 429 (Problem) — rate-limited — the key's request budget is spent; wait Retry-After seconds.
- 500 (Problem) — internal.
