# impartire API — Reference: Collections

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/collections

List the org's collections

Ordered by name unless `sort` says otherwise. To poll for changes, pass `updated_after` with the newest `updated_at` you have seen and `sort=-updated_at` (or `updated_at`); `updated_at` moves when the name, description or constraints change, not when items do. A deleted collection leaves the list. For an OAuth token, the collections filed in a project its person can open.

Scopes: `collections:read`

Parameters:

- `project_id` (query, string (uuid)) — Only collections filed in this project (GET /api/v1/projects). Not its sub-projects.
- `parent_id` (query, string (uuid)) — Only the collections nested directly in this one.
- `root_only` (query, "true" | "false") — `true`: only collections not nested in another. Default false.
- `q` (query, string) — Case-insensitive substring of the collection's name. Characters are matched literally.
- `updated_after` (query, string (date-time)) — Only collections changed strictly after this instant (ISO 8601 with offset), compared to the millisecond — poll with the newest `updated_at` you have seen (that collection does not come back), sorted `-updated_at` or `updated_at`.
- `sort` (query, "name" | "-name" | "updated_at" | "-updated_at") — Sort key; a leading `-` is descending. Default `name`. 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 — `CollectionList`

- `data` (array of Collection, required)
  - `id` (string (uuid), required)
  - `name` (string, required)
  - `description` (string or null, required)
  - `project` (ProjectRef or null, required) — The project the collection is filed in — its permission parent: who may open the project may open the collection. Null only for an OAuth token whose person owns the collection but cannot open its project.
    - `id` (string (uuid), required)
    - `name` (string, required)
  - `parent_id` (string (uuid) or null, required) — The collection this one is nested in (GET /collections/{id}/children lists a parent's children), or null for a top-level collection.
  - `status` ("draft" | "published", required) — `draft` until published to consumers. Publishing and archiving are done in the app.
  - `allowed_type_ids` (array of string (uuid) or null, required) — When set, only assets of these asset types (GET /api/v1/asset-types) may be added. Null or empty: any type.
  - `allowed_category_ids` (array of string (uuid) or null, required) — When set, only assets assigned at least one of these org categories may be added. Null or empty: any.
  - `item_count` (integer, required) — How many items the collection holds (GET /collections/{id}/items lists them).
  - `created_at` (string (date-time), required)
  - `updated_at` (string (date-time), required) — When its name, description or constraints last changed. Adding, removing or reordering items does not move it (the ETag moves instead).
- `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 `limit`, `project_id`, `parent_id`, `root_only`, `updated_after`, `sort` or `cursor`.
- 429 (Problem) — rate-limited — the key's request budget is spent; wait Retry-After seconds.
- 500 (Problem) — internal.

#### POST /api/v1/collections

Create a collection, filed in a project and optionally nested in another

Born a draft. `project_id` is its permission parent (omitted: the org's Unfiled project); `parent_id` nests it for browsing only. The credential is the collection's owner: for an API key, the key's service principal. An OAuth token's person needs create_collection in the org, contribute on a named project, and update on a named parent collection.

Scopes: `collections: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 — `CreateCollectionRequest`

- `name` (string, required)
- `description` (string or null)
- `project_id` (string (uuid)) — The project to file it in (GET /api/v1/projects) — its permission parent. Omit to file it in the org's Unfiled project, which every member can open.
- `parent_id` (string (uuid)) — Nest it in this collection (GET /api/v1/collections). The nesting is for browsing only: who may open it is decided by its own project.
- `allowed_type_ids` (array of string (uuid)) — Only assets of these types may be added (GET /api/v1/asset-types).
- `allowed_category_ids` (array of string (uuid)) — Only assets assigned at least one of these org categories may be added.

Response 201 — `Collection`: A curated, ordered set of assets.

- `id` (string (uuid), required)
- `name` (string, required)
- `description` (string or null, required)
- `project` (ProjectRef or null, required) — The project the collection is filed in — its permission parent: who may open the project may open the collection. Null only for an OAuth token whose person owns the collection but cannot open its project.
  - `id` (string (uuid), required)
  - `name` (string, required)
- `parent_id` (string (uuid) or null, required) — The collection this one is nested in (GET /collections/{id}/children lists a parent's children), or null for a top-level collection.
- `status` ("draft" | "published", required) — `draft` until published to consumers. Publishing and archiving are done in the app.
- `allowed_type_ids` (array of string (uuid) or null, required) — When set, only assets of these asset types (GET /api/v1/asset-types) may be added. Null or empty: any type.
- `allowed_category_ids` (array of string (uuid) or null, required) — When set, only assets assigned at least one of these org categories may be added. Null or empty: any.
- `item_count` (integer, required) — How many items the collection holds (GET /collections/{id}/items lists them).
- `created_at` (string (date-time), required)
- `updated_at` (string (date-time), required) — When its name, description or constraints last changed. Adding, removing or reordering items does not move it (the ETag moves instead).

Other responses:

- 401 (Problem) — unauthenticated — no key, or an unknown, revoked or expired one.
- 403 (Problem) — insufficient-scope, or insufficient-permission — an OAuth token's person may not create collections, add to that project, or nest in that collection.
- 404 (Problem) — not-found.
- 409 (Problem) — idempotency-in-flight.
- 422 (Problem) — validation (name, description, project_id, parent_id, allowed_type_ids, allowed_category_ids), or idempotency-mismatch.
- 429 (Problem) — rate-limited — the key's request budget is spent; wait Retry-After seconds.
- 500 (Problem) — authorization-write-failed — the collection's permissions could not be written, so it was rolled back; or internal.

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

Get one collection

Scopes: `collections:read`

Parameters:

- `id` (path, string (uuid), required) — The collection's id, from GET /api/v1/collections.
- `If-None-Match` (header, string) — An ETag from a previous response; 304 if unchanged.

Response 200 — `Collection`: A curated, ordered set of assets.

- `id` (string (uuid), required)
- `name` (string, required)
- `description` (string or null, required)
- `project` (ProjectRef or null, required) — The project the collection is filed in — its permission parent: who may open the project may open the collection. Null only for an OAuth token whose person owns the collection but cannot open its project.
  - `id` (string (uuid), required)
  - `name` (string, required)
- `parent_id` (string (uuid) or null, required) — The collection this one is nested in (GET /collections/{id}/children lists a parent's children), or null for a top-level collection.
- `status` ("draft" | "published", required) — `draft` until published to consumers. Publishing and archiving are done in the app.
- `allowed_type_ids` (array of string (uuid) or null, required) — When set, only assets of these asset types (GET /api/v1/asset-types) may be added. Null or empty: any type.
- `allowed_category_ids` (array of string (uuid) or null, required) — When set, only assets assigned at least one of these org categories may be added. Null or empty: any.
- `item_count` (integer, required) — How many items the collection holds (GET /collections/{id}/items lists them).
- `created_at` (string (date-time), required)
- `updated_at` (string (date-time), required) — When its name, description or constraints last changed. Adding, removing or reordering items does not move it (the ETag moves instead).

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

Rename or describe a collection, or change its constraints

If-Match is required: send the ETag from the last GET (or from the create). Changing a constraint does not remove items already in the collection. `status` is not settable — publishing and archiving are lifecycle acts done in the app — and an unknown field is a 422. An OAuth token's person needs update on the collection.

Scopes: `collections:write`

Parameters:

- `id` (path, string (uuid), required) — The collection's id, from GET /api/v1/collections.
- `If-Match` (header, string, required) — The collection's ETag.

Request body — `UpdateCollectionRequest`: At least one of name, description, allowed_type_ids, allowed_category_ids. `status` is not settable: publishing and archiving are lifecycle acts done in the app. Moving a collection is not in v1.

- `name` (string)
- `description` (string or null) — null clears it.
- `allowed_type_ids` (array of string (uuid) or null) — null or [] lifts the constraint. Items already in the collection stay.
- `allowed_category_ids` (array of string (uuid) or null) — null or [] lifts the constraint. Items already in the collection stay.

Response 200 — `Collection`: A curated, ordered set of assets.

- `id` (string (uuid), required)
- `name` (string, required)
- `description` (string or null, required)
- `project` (ProjectRef or null, required) — The project the collection is filed in — its permission parent: who may open the project may open the collection. Null only for an OAuth token whose person owns the collection but cannot open its project.
  - `id` (string (uuid), required)
  - `name` (string, required)
- `parent_id` (string (uuid) or null, required) — The collection this one is nested in (GET /collections/{id}/children lists a parent's children), or null for a top-level collection.
- `status` ("draft" | "published", required) — `draft` until published to consumers. Publishing and archiving are done in the app.
- `allowed_type_ids` (array of string (uuid) or null, required) — When set, only assets of these asset types (GET /api/v1/asset-types) may be added. Null or empty: any type.
- `allowed_category_ids` (array of string (uuid) or null, required) — When set, only assets assigned at least one of these org categories may be added. Null or empty: any.
- `item_count` (integer, required) — How many items the collection holds (GET /collections/{id}/items lists them).
- `created_at` (string (date-time), required)
- `updated_at` (string (date-time), required) — When its name, description or constraints last changed. Adding, removing or reordering items does not move it (the ETag moves instead).

Other responses:

- 401 (Problem) — unauthenticated — no key, or an unknown, revoked or expired one.
- 403 (Problem) — insufficient-scope, or insufficient-permission — an OAuth token's person may open the collection but not change it.
- 404 (Problem) — not-found.
- 412 (Problem) — precondition-failed — If-Match is stale; `etag` in the body is the current one.
- 422 (Problem) — validation (empty body, an unknown field such as `status`, name, description, allowed_type_ids, allowed_category_ids).
- 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/collections/{id}

Delete a collection

Soft-deletes the collection; it no longer appears in reads or lists. Its assets are untouched. No If-Match: a second DELETE is a 404. An OAuth token's person needs delete on the collection.

Scopes: `collections:write`

Parameters:

- `id` (path, string (uuid), required) — The collection's id, from GET /api/v1/collections.

Other responses:

- 204 — Deleted.
- 401 (Problem) — unauthenticated — no key, or an unknown, revoked or expired one.
- 403 (Problem) — insufficient-scope, or insufficient-permission — an OAuth token's person may open the collection but not delete it.
- 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/collections/{id}/children

List the collections nested in a collection

The collections nested directly in this one, by name. For an OAuth token, only those its person can open.

Scopes: `collections:read`

Parameters:

- `id` (path, string (uuid), required) — The collection's id, from GET /api/v1/collections.
- `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 — `CollectionList`

- `data` (array of Collection, required)
  - `id` (string (uuid), required)
  - `name` (string, required)
  - `description` (string or null, required)
  - `project` (ProjectRef or null, required) — The project the collection is filed in — its permission parent: who may open the project may open the collection. Null only for an OAuth token whose person owns the collection but cannot open its project.
    - `id` (string (uuid), required)
    - `name` (string, required)
  - `parent_id` (string (uuid) or null, required) — The collection this one is nested in (GET /collections/{id}/children lists a parent's children), or null for a top-level collection.
  - `status` ("draft" | "published", required) — `draft` until published to consumers. Publishing and archiving are done in the app.
  - `allowed_type_ids` (array of string (uuid) or null, required) — When set, only assets of these asset types (GET /api/v1/asset-types) may be added. Null or empty: any type.
  - `allowed_category_ids` (array of string (uuid) or null, required) — When set, only assets assigned at least one of these org categories may be added. Null or empty: any.
  - `item_count` (integer, required) — How many items the collection holds (GET /collections/{id}/items lists them).
  - `created_at` (string (date-time), required)
  - `updated_at` (string (date-time), required) — When its name, description or constraints last changed. Adding, removing or reordering items does not move it (the ETag moves instead).
- `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 `limit` or `cursor`.
- 429 (Problem) — rate-limited — the key's request budget is spent; wait Retry-After seconds.
- 500 (Problem) — internal.

#### GET /api/v1/collections/{id}/items

List a collection's items in order

Ordered by `sort_order`, then by when each was added. Paginated: the list is bounded by the collection, a page at a time. Each item names its asset's current version. For an OAuth token, an item whose asset its person may not read keeps its place and id but is `restricted` with `asset: null`.

Scopes: `collections:read`

Parameters:

- `id` (path, string (uuid), required) — The collection's id, from GET /api/v1/collections.
- `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 — `CollectionItemList`

- `data` (array of CollectionItem, required)
  - `id` (string (uuid), required) — The item's id — what DELETE …/items/{item_id} and PUT …/items/order take.
  - `asset` (object or null, required) — The asset (its current version). Null when `restricted`.
    - `id` (string (uuid), required)
    - `name` (string, required)
    - `type` (object, required)
      - `id` (string (uuid), required)
      - `name` (string, required)
  - `restricted` (boolean, required) — True for an OAuth token whose person may not read the asset: the item keeps its place and id (a curator can still move or remove it) but not the asset's identity. Always false for an API key.
  - `sort_order` (integer, required) — The item's position key; items are listed by it, ascending.
  - `added_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 `limit` or `cursor`.
- 429 (Problem) — rate-limited — the key's request budget is spent; wait Retry-After seconds.
- 500 (Problem) — internal.

#### POST /api/v1/collections/{id}/items

Add an asset to a collection

Pins the asset's current version. Without `sort_order` the item goes after the last one. The collection's constraints apply: a type not in `allowed_type_ids`, an asset with none of `allowed_category_ids`, or an unpublished asset in a published collection is 422 `collection-constraint`, `errors[].path` naming the rule. An asset already in the collection (any version of it) is 409 `already-member`. An OAuth token's person needs update on the collection and read on the asset.

Scopes: `collections:write`

Parameters:

- `id` (path, string (uuid), required) — The collection's id, from GET /api/v1/collections.
- `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 — `AddCollectionItemRequest`

- `asset_id` (string (uuid), required) — The asset to add (its current version's id or its root id; the item pins the current version). GET /api/v1/assets lists them.
- `sort_order` (integer) — Its position key. Omit to append after the last item.

Response 201 — `CollectionItem`: One item of a collection: an asset at a position.

- `id` (string (uuid), required) — The item's id — what DELETE …/items/{item_id} and PUT …/items/order take.
- `asset` (object or null, required) — The asset (its current version). Null when `restricted`.
  - `id` (string (uuid), required)
  - `name` (string, required)
  - `type` (object, required)
    - `id` (string (uuid), required)
    - `name` (string, required)
- `restricted` (boolean, required) — True for an OAuth token whose person may not read the asset: the item keeps its place and id (a curator can still move or remove it) but not the asset's identity. Always false for an API key.
- `sort_order` (integer, required) — The item's position key; items are listed by it, ascending.
- `added_at` (string (date-time), required)

Other responses:

- 401 (Problem) — unauthenticated — no key, or an unknown, revoked or expired one.
- 403 (Problem) — insufficient-scope, or insufficient-permission — an OAuth token's person may open the collection but not change it.
- 404 (Problem) — not-found.
- 409 (Problem) — already-member — the asset is already in the collection (`item_id` in the body); or idempotency-in-flight.
- 422 (Problem) — collection-constraint, unknown-asset, validation (asset_id, sort_order), or idempotency-mismatch.
- 429 (Problem) — rate-limited — the key's request budget is spent; wait Retry-After seconds.
- 500 (Problem) — internal.

#### DELETE /api/v1/collections/{id}/items/{item_id}

Remove an item from a collection

The asset itself is untouched. A second DELETE is a 404. An OAuth token's person needs update on the collection.

Scopes: `collections:write`

Parameters:

- `id` (path, string (uuid), required) — The collection's id, from GET /api/v1/collections.
- `item_id` (path, string (uuid), required) — The item's id, from GET /api/v1/collections/{id}/items.

Other responses:

- 204 — Removed.
- 401 (Problem) — unauthenticated — no key, or an unknown, revoked or expired one.
- 403 (Problem) — insufficient-scope, or insufficient-permission — an OAuth token's person may open the collection but not change it.
- 404 (Problem) — not-found.
- 429 (Problem) — rate-limited — the key's request budget is spent; wait Retry-After seconds.
- 500 (Problem) — internal.

#### PUT /api/v1/collections/{id}/items/order

Put a collection's items in a new order

`item_ids` is every item of the collection, each exactly once, in the new order; their `sort_order` becomes 0, 1, 2… Anything else (a missing, repeated or foreign id) is a 422. A collection of more than 1000 items is reordered in the app. Answers the whole list after. If an item was added or removed meanwhile, nothing moves and the answer is 409 `conflict`: read the items and send the order again. Safe to repeat: no Idempotency-Key, no If-Match. An OAuth token's person needs update on the collection.

Scopes: `collections:write`

Parameters:

- `id` (path, string (uuid), required) — The collection's id, from GET /api/v1/collections.

Request body — `ReorderCollectionItemsRequest`

- `item_ids` (array of string (uuid), required) — Every item id of the collection, each exactly once, in the new order (at most 1000). The items' sort_order becomes 0, 1, 2… in this order.

Response 200 — `CollectionItemList`

- `data` (array of CollectionItem, required)
  - `id` (string (uuid), required) — The item's id — what DELETE …/items/{item_id} and PUT …/items/order take.
  - `asset` (object or null, required) — The asset (its current version). Null when `restricted`.
    - `id` (string (uuid), required)
    - `name` (string, required)
    - `type` (object, required)
      - `id` (string (uuid), required)
      - `name` (string, required)
  - `restricted` (boolean, required) — True for an OAuth token whose person may not read the asset: the item keeps its place and id (a curator can still move or remove it) but not the asset's identity. Always false for an API key.
  - `sort_order` (integer, required) — The item's position key; items are listed by it, ascending.
  - `added_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, or insufficient-permission — an OAuth token's person may open the collection but not change it.
- 404 (Problem) — not-found.
- 409 (Problem) — conflict — the collection's items changed while it was being reordered; nothing moved.
- 422 (Problem) — validation — `item_ids` is not every item exactly once, or the collection is too large to reorder here.
- 429 (Problem) — rate-limited — the key's request budget is spent; wait Retry-After seconds.
- 500 (Problem) — internal.
