# Session files

Read the Documents, Mockups, Images and Videos the agent wrote in a chat session. List them to get their ids, then read one, by its id or its name, for its content. They stay readable after the session ends, and reading them never wakes a sandbox.

| Method | Path                                                                         | Description                                                                                                                                                                                                                                                                                                                                                                    |
| ------ | ---------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| GET    | `/api/projects/{projectId}/chat-sessions/{sessionId}/artifacts`              | Use `sessions files list`. List the session's files of the four kinds an agent writes.                                                                                                                                                                                                                                                                                         |
| POST   | `/api/projects/{projectId}/chat-sessions/{sessionId}/artifacts`              | Use `sessions files attach`. Point a Backlog session at a file of the project's Files, or at each file directly in a folder. Answers with one ref per file.                                                                                                                                                                                                                    |
| GET    | `/api/projects/{projectId}/chat-sessions/{sessionId}/artifacts/{artifactId}` | Use `sessions files get`. Read one file of the session by its id or its name: a deleted one, or one of another kind, is not found.                                                                                                                                                                                                                                             |
| GET    | `/api/projects/{projectId}/chat-sessions/{sessionId}/files`                  | List the session's files: what its agent wrote, most recently changed first, a deleted one as a row that says who deleted it, then the files its ticket points at. Rows only: a body or a file url comes from reading one.                                                                                                                                                     |
| POST   | `/api/projects/{projectId}/chat-sessions/{sessionId}/files`                  | Point a Backlog session at a file of the project's Files, or at each file directly in a folder, without copying it: its agent reads the current version when it starts. Markdown, HTML, images and videos only. The batch is refused whole on another type, on a file this session's own agent wrote, and on a name that clashes with another of the session's attached files. |
| GET    | `/api/projects/{projectId}/chat-sessions/{sessionId}/files/{fileId}`         | Read one file of the session, by its id or its name, such as `testing-checklist`, `qa-report.html` or `review`: a Markdown or HTML file carries its body, any other a presigned url, its media type and its size. A deleted one says who deleted it and holds no content.                                                                                                      |
| POST   | `/api/prototypes/render`                                                     | Render a prototype from its prototype.json, content.css and content.js, attached as three files, and save it to a Chat Session's files (projectId and sessionId) or a workspace's Files (workspaceId). A failed check answers 422 with the issues in details and saves nothing. A session token saves only to its own session, Sensei only to Files.                           |

## Kinds

| `kind` | In the app | Where the content is |
| --- | --- | --- |
| `markdown` | Document | Markdown text in `body`. |
| `html` | Mockup | A self-contained HTML page in `body`. |
| `image` | Image | A file behind `url`, with its `mediaType` and `byteSize`. |
| `video` | Video | A file behind `url`, with its `mediaType` and `byteSize`. |
| `file` | File | A file behind `url`. A member replaced the agent's file with one of another type, such as a PDF. |

The field a kind does not use comes back null.

## List a session's files

`GET .../chat-sessions/{sessionId}/files` lists what the session's agent wrote, most recently changed first, then the files its ticket points at. It holds no content.

```bash
curl -s "https://app.kosuke.ai/api/projects/$PROJECT_ID/chat-sessions/$SESSION_ID/files" \
  -H "Authorization: Bearer $KOSUKE_API_KEY"
```

```json
{
  "data": {
    "files": [
      {
        "id": "e2c79a10-5b4d-4f8e-a3c6-91d0b7f2e845",
        "slug": "checkout-investigation",
        "title": "Why checkout fails on Safari",
        "kind": "markdown",
        "version": 2,
        "name": "checkout-investigation.md",
        "path": "Sessions/storefront/Fix checkout on Safari/checkout-investigation.md",
        "mediaType": "text/markdown",
        "byteSize": 4120,
        "locked": false,
        "qaEvidence": false,
        "reference": false,
        "updatedBy": { "id": "user_2x9", "name": "Sam Lee" },
        "expiredAt": null,
        "deleted": null,
        "replaced": null,
        "createdAt": "2026-09-30T08:02:13.415Z",
        "updatedAt": "2026-09-30T08:47:55.902Z"
      }
    ]
  }
}
```

| Field | What it says |
| --- | --- |
| `slug` | The name the agent writes. When the agent writes the same `slug` again, `version` goes up by one. Only the QA Report takes the version it declares. Only the current version is kept. |
| `title` | The agent's heading for the file. A rename in Files leaves it as it is. |
| `name`, `path` | The file's name and place in Files as they are now. |
| `locked` | The QA Report, the Testing Checklist and QA's recordings. Only Sensei changes them. |
| `qaEvidence` | Listed in the QA evidence group: locked, and not the QA Report. |
| `reference` | A file of Files the ticket points at, which this session did not write. |
| `deleted` | `{ by, at }` once a member deleted it. The row stays and holds no content. |
| `replaced` | `{ by, at }` once a member replaced it. The version the agent wrote is not kept. |
| `expiredAt` | When retention deleted a recording's bytes, else null. |

The CLI command is `kosuke sessions files list <projectId> <sessionId>`.

## Read one file

`GET .../chat-sessions/{sessionId}/files/{fileId}` returns the file with its content, by its id, its `slug` or its name with the extension. A Document or a Mockup carries its text in `body`, with `url` null. An Image or a Video carries a download URL in `url`, with `body` null:

```json
{
  "data": {
    "id": "7d3f0b62-c18e-4a95-b2d7-4e6a8c1f09b3",
    "slug": "settings-dark-mode",
    "title": "Settings page in dark mode",
    "kind": "image",
    "version": 1,
    "name": "settings-dark-mode.png",
    "mediaType": "image/png",
    "byteSize": 184233,
    "body": null,
    "url": "https://…",
    "expiredAt": null,
    "deleted": null,
    "replaced": null
  }
}
```

The download URL works for 24 hours. Read the file again for a fresh one. Request a file through its own session, because any other session answers `404`. The CLI command is `kosuke sessions files get <projectId> <sessionId> <fileId>`.

A deleted file still answers **200**, with `deleted` set and no content. An expired recording also answers **200**, with `url: null` and `expiredAt` set.

`GET .../chat-sessions/{sessionId}/files/{fileId}/download` downloads the source or media bytes. An expired recording answers **410** (`GONE`) with `Video expired on <date>: videos are kept 30 days.`; a missing or deleted file answers **404**. Cron Run and shared-link downloads answer the same way.

## Point a ticket at a file

`POST .../chat-sessions/{sessionId}/files` with `{ "file": "prototypes/board/a-columns.html" }` points a Backlog session at a file of the project's Files, or at each file directly in a folder. Nothing is copied: the agent reads the current version when the session starts. A personal space reads your Personal Files. The CLI command is `kosuke sessions files attach <projectId> <sessionId> --file <path>`.

The batch is refused whole on an unsupported type, on a file this session's own agent wrote, and on a name that clashes with another attached file. See [Put a file on a ticket](https://kosuke.ai/docs/sessions/files.md#put-a-file-on-a-ticket).

## Who can read them

Any member of the workspace can read the files of any session in it. A share link also opens the files the session's agent wrote to people outside the workspace, as [Previews](https://kosuke.ai/docs/endpoint-reference/previews.md) describes.

The agent writes files during a turn. To get one, ask for it in a message, such as "Write your findings up as a document." To change one in Files, use [`kosuke files`](https://kosuke.ai/docs/interfaces/cli.md#files). A locked file or a folder Sensei keeps answers `403`.

## Older routes

The `.../artifacts` routes stay served for installed clients, and are deprecated. They keep their older response shape, under `artifacts`. A list holds only live files and references of the four kinds. A read answers `404` for a deleted file or one of another kind. `POST` records references and answers with them as refs.

## Errors

| Status | Code | When |
| --- | --- | --- |
| 404 | `NOT_FOUND` | The project, session or file does not exist, the file belongs to another session, or its download object is missing. |
| 410 | `GONE` | The downloaded recording has expired. A read of the file still answers 200. |

[Responses and errors](https://kosuke.ai/docs/api/errors.md) has the full table.

## Next steps

- [Files of a session](https://kosuke.ai/docs/sessions/files.md)
- [Previews](https://kosuke.ai/docs/endpoint-reference/previews.md)
- [Messages and turns](https://kosuke.ai/docs/endpoint-reference/messages-and-turns.md)
