# Media

A file attached to a record: the metadata of one upload (its name, type, size and the collection it belongs to) plus an on-demand signed download URL. Files are reached only inside their parent's collection, `work-items/{work_item}/attachments` or `drive-items/{drive_item}/files`. The API never streams bytes: uploading is a two-step flow, where you create an upload ticket on the parent, PUT the raw bytes to the ticket's own signed URL outside this API, then POST the ticket's id as `upload_id` to the collection. That way `/api/v2` still accepts exactly one request content type, `application/json`.

## Used by
- GET /api/v2/drive-items/{drive_item}/files: List a drive item's files (MediaList)
- POST /api/v2/drive-items/{drive_item}/files: Attach an uploaded file to a drive item (Media)
- GET /api/v2/work-items/{work_item}/attachments: List a work item's attachments (MediaList)
- POST /api/v2/work-items/{work_item}/attachments: Attach an uploaded file to a work item (Media)

## Media fields
- id (integer, required): Unique identifier.
- name (string ≤ 255, required): File name as uploaded.
- mime_type (string or null ≤ 255, required): IANA media type.
- size (integer, required): Size in bytes.
- disk (string ≤ 255, required): Name of the storage location the file is kept in.
- collection (string ≤ 255, required): Media collection the file belongs to.
- created_at (timestamp (YYYY-MM-DDThh:mm:ssZ), required): Upload timestamp.
- updated_at (timestamp (YYYY-MM-DDThh:mm:ssZ), required): Last-write timestamp; use for incremental sync.
- url (string or null, optional, on request): Signed download URL, requiring your bearer token. Request with `?include=url`. Null only where your organisation's file storage cannot sign URLs, in which case the response carries a `signed_url_unavailable` warning and downloads are not available through the API.
- url_expires_at (timestamp (YYYY-MM-DDThh:mm:ssZ) or null, optional, on request): When `url` stops working. Always `null` in v2.0: the signed route the URL points at does not expire. Declared so a future bounded-TTL download path is an additive change.

## Variants

### MediaList

What a collection returns, and what `?view=summary` returns on a single record.

10 fields.
Identical to the detail object above.

### MediaSummary

The embedded form: what you get when another resource carries this one and you did not `include` it.

3 fields.
Omits: size, disk, collection, created_at, updated_at, url, url_expires_at

## Columns deliberately not published
- conversions_disk: Used only by plnnrly's file storage.
- custom_properties: Used only by plnnrly's file storage.
- file_name: Storage file name; use `name`.
- generated_conversions: Used only by plnnrly's file storage.
- manipulations: Used only by plnnrly's file storage.
- model_id: The other half of that parent reference, withheld with it.
- model_type: A file is only ever reached inside its parent's collection, so the parent is the path rather than a payload field.
- order_column: The file's position as arranged in plnnrly, which the API does not use.
- responsive_images: Used only by plnnrly's file storage.
- uuid: A second identifier used only by plnnrly's file storage; `id` is the one to use.

## Example
```json
{
  "id": 1,
  "name": "…",
  "mime_type": "…",
  "size": 1,
  "disk": "…",
  "collection": "…",
  "created_at": "2026-03-04T09:15:00Z",
  "updated_at": "2026-03-04T09:15:00Z",
  "url": "…",
  "url_expires_at": "2026-03-04T09:15:00Z"
}
```
