> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pinework.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# File

> A file is bytes stored in your workspace, with a visibility, numbered revisions, and relations to tasks, projects, runs, comments and conversations.

A file is an uploaded blob you attach to other objects. Its id is `file_` plus 16 letters and digits, and each revision has a `frev_…` id. Files show up next to the [tasks](/reference/task) they are attached to, and wiki pages can link to them.

## States

A file has no status field. Its state comes from `deletedAt` and from whether the row still exists.

| State | Meaning | Ends here |
| - | - | - |
| Active | `deletedAt` is null. You can read, download and change it. | No |
| Deleted | `deletedAt` is set. It is left out of lists and reads. | No |
| Purged | The sweep removed the row and every revision's bytes. | Yes |

| From | To | Trigger |
| - | - | - |
| (none) | Active | `pinework file upload` or `POST /api/v1/files`. |
| Active | Deleted | `pinework file delete` or `file bulk-delete`. |
| Deleted | Active | `pinework file restore`, by the owner or a workspace admin, while the bytes still exist. |
| Deleted | Purged | The sweep, 10 minutes after deletion. |

`file delete` also removes the current bytes at once, so a restore after it fails with `restore_r2_gone`. `file bulk-delete` keeps the bytes until the sweep. Deleting a file also detaches its relations.

## Visibility

| `visibility` | Who can read it |
| - | - |
| `company` | Every member of the workspace. This is the default. |
| `private` | The owner and workspace admins. Other members cannot read it. |

Setting `private` takes the owner or a workspace admin. A file an agent uploads has no owner, so `ownerUserId` is null.

## Relations and revisions

A relation attaches a file to one object under a role you name, such as `attachment` or `spec`. `entityType` is `task`, `project`, `run`, `comment` or `conversation`. `entityId` takes a `tsk_`, `PIN-`, `prj_`, `run_`, `cnv_` or `msg_` id. `file upload` attaches to the current task with role `attachment` unless you say otherwise.

`file replace` writes new bytes as the next version and raises `revisionCount` by one. While the file is active, you can download any earlier version by its `frev_` id.

## Fields

<ResponseField name="id" type="string" required>File id, `file_…`.</ResponseField>
<ResponseField name="object" type="string" required>Always `file`.</ResponseField>
<ResponseField name="name" type="string" required>File name, up to 255 characters.</ResponseField>
<ResponseField name="contentType" type="string">MIME type. The server detects it from the name when you send none. Null when unknown.</ResponseField>
<ResponseField name="sizeBytes" type="integer" required>Size of the current version in bytes.</ResponseField>
<ResponseField name="description" type="string">Your description. Null when not set.</ResponseField>
<ResponseField name="ownerUserId" type="string">Owner, `usr_…`. Null when an agent uploaded it.</ResponseField>
<ResponseField name="visibility" type="string" required>`company` or `private`.</ResponseField>
<ResponseField name="deletedAt" type="string">Deletion time. Null while active.</ResponseField>
<ResponseField name="createdBy" type="object">Uploader: `type` (`user` or `agent`), `id` and `name`. Null when unresolved.</ResponseField>
<ResponseField name="sha256" type="string">SHA-256 of the current bytes. Null when not computed.</ResponseField>
<ResponseField name="textPreview" type="string">Short text excerpt for text files. Null otherwise.</ResponseField>
<ResponseField name="revisionCount" type="integer" required>Number of versions. The current version has this number.</ResponseField>
<ResponseField name="content" type="string">Full text. Present only with `expand[]=content` on a text file.</ResponseField>

<ResponseField name="relations" type="object[]">
  Attachments. Present only with `expand[]=relations`.

  <Expandable title="relation">
    <ResponseField name="id" type="string" required>Relation id, a UUID.</ResponseField>
    <ResponseField name="fileId" type="string" required>The file, `file_…`.</ResponseField>
    <ResponseField name="entityType" type="string" required>`task`, `project`, `run`, `comment` or `conversation`.</ResponseField>
    <ResponseField name="entityId" type="string" required>Public id of the attached object.</ResponseField>
    <ResponseField name="role" type="string" required>Role you named.</ResponseField>
    <ResponseField name="createdAt" type="string" required>Attach time.</ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="createdAt" type="string" required>Upload time.</ResponseField>
<ResponseField name="updatedAt" type="string" required>Last change time.</ResponseField>

A list returns only `object`, `id`, `name`, `contentType`, `sizeBytes`, `description`, `createdAt` and `updatedAt`. A revision (`file_revision`) has `id`, `fileId`, `version`, `sizeBytes`, `contentType`, `sha256`, `author` and `createdAt`.

## CLI

| Command | Does |
| - | - |
| `file upload` | Uploads a local file and attaches it to the current task by default. |
| `file list` | Lists files, newest first. Filter by `--task`, `--project`, `--entity-type` with `--entity-id`, `--q` or `--status deleted`. |
| `file get` | Shows metadata for one or more ids. `--expand content` or `--expand relations`. |
| `file read` | Prints the text content, else the preview. |
| `file download` | Writes the bytes to disk. `--revision` picks an earlier version. |
| `file history` | Lists versions, newest first. |
| `file replace` | Uploads new bytes as a new version. |
| `file relate` / `bulk-relate` | Attaches one file, or up to 100, to an object under a role. |
| `file relations list` | Lists what a file is attached to. |
| `file delete` / `bulk-delete` | Deletes one file, or up to 100. |
| `file restore` | Restores a deleted file. |
| `file transfer-owner` | Makes another member the owner. |

```bash theme={null}
pinework file upload findings.md --task PIN-42 --description "Load test results"
pinework file replace file_P5enhziIpx5qrxbb findings.md
pinework file history file_P5enhziIpx5qrxbb
pinework file relate file_P5enhziIpx5qrxbb --entity-type project --entity-id prj_Q7rT2mXk9LpA4vBn --role spec
```

## API

| Method | Path | Does |
| - | - | - |
| `GET` | `/api/v1/files` | Lists files. Query: `limit`, `after`, `q`, `entityType`, `entityId`, `status=deleted`, `ids`. |
| `POST` | `/api/v1/files` | Uploads a file from `name` and `contentBase64`, with optional `relations`. |
| `GET` | `/api/v1/files/{id}` | Gets one file. Accepts `expand[]` and `includeDeleted`. |
| `PATCH` | `/api/v1/files/{id}` | Changes `name`, `description` or `visibility`. |
| `DELETE` | `/api/v1/files/{id}` | Deletes the file. Returns 204. |
| `POST` | `/api/v1/files/{id}/restore` | Restores a deleted file. |
| `GET` | `/api/v1/files/{id}/download` | Returns a signed download `url` and its `expiresAt`. |
| `GET` | `/api/v1/files/{id}/revisions` | Lists versions, newest first. |
| `GET` | `/api/v1/files/{id}/revisions/{revisionId}/download` | Returns a signed URL for one version. |
| `POST` | `/api/v1/files/{id}/replace` | Writes new bytes from `contentBase64` as the next version. |
| `POST` | `/api/v1/files/{id}/transfer-owner` | Sets the owner to `newOwnerId`. |
| `POST` | `/api/v1/files/bulk-delete` | Deletes the files in `fileIds`. |
| `POST` | `/api/v1/files/bulk-relate` | Attaches the files in `fileIds` to one object. |
| `GET` | `/api/v1/files/{id}/relations` | Lists relations. |
| `POST` | `/api/v1/files/{id}/relations` | Adds a relation from `entityType`, `entityId` and `role`. |
| `GET` | `/api/v1/files/{id}/relations/{relationId}` | Gets one relation. |
| `DELETE` | `/api/v1/files/{id}/relations/{relationId}` | Removes one relation. Returns 204. |

## Errors

| Code | HTTP | When | What to do |
| - | - | - | - |
| `content_required` | 400 | An upload has no `contentBase64`. | Send the bytes. |
| `validation_error` | 413 | The bytes are over the size cap. | Split or shrink the file. |
| `expand_unknown` | 400 | An `expand[]` value is not `content` or `relations`. | Fix the value. |
| `expand.content_too_large` | 400 | The file is over the inline content cap. | Use `file download`. |
| `expand.not_allowed_on_list` | 400 | A list call sends `expand[]`. | Expand on a get. |
| `list_deleted_forbidden` | 403 | An agent lists deleted files. | List as a person. |
| `visibility_flip_forbidden` | 403 | A non-owner, non-admin sets `private`. | Ask the owner or an admin. |
| `restore_forbidden` | 403 | A non-owner, non-admin restores. | Ask the owner or an admin. |
| `not_deleted` | 409 | You restore an active file. | Nothing to do. |
| `restore_r2_gone` | 422 | The bytes are already removed. | Upload the file again. |
| `kind_mismatch` | 400 | The file has no stored bytes. | Upload a new file. |
| `owner_transfer.forbidden` | 403 | A non-owner, non-admin transfers. | Ask the owner or an admin. |
| `owner_transfer.same_owner` | 409 | The new owner already owns it. | Nothing to do. |
| `owner_transfer.user_not_found` | 404 | The new owner is not a member. | Pick a member. |
| `duplicate_relation` | 409 | The relation already exists. | Nothing to do. |
| `relation_not_found` | 404 | No such relation on this file. | List relations for the id. |
| `storage_unavailable` | 503 | Storage could not be reached. | Retry. |
| `sweeper_not_registered` | 503 | The server is starting up. | Retry shortly. |

Bulk result rows carry `file_not_found`, `duplicate_relation` or `internal_error` per id. For shared codes, see [API errors](/reference/api#errors).

## Limits

* 50 MB (52,428,800 bytes) per upload or replace.
* Inline content: 50 KB (51,200 bytes) for an agent, 1 MB (1,048,576 bytes) for a person.
* A signed download URL lasts 300 seconds.
* A deleted file is purged 10 minutes after deletion.
* 100 ids per bulk call.
* A list returns 25 files by default and 100 at most. A relation list returns 50 by default and 100 at most.
* `name` holds 255 characters, `description` 2,000, `role` 1 to 100, and `q` 100.

<CardGroup cols={2}>
  <Card title="Task" href="/reference/task">
    The object you most often attach files to.
  </Card>

  <Card title="Wiki" href="/reference/wiki">
    Pages that can link to files.
  </Card>

  <Card title="Conversation" href="/reference/conversation">
    Files attached to messages.
  </Card>

  <Card title="Run" href="/reference/run">
    Files a run produced.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.