# Files and task state

Each project has a disk with three parts: key-value entries, files, and task state. All of them outlive the session and are shared by every key of the project with the `disk` scope.

## Key-value

Small values, optionally with an expiry:

```text
kv_set({key: "last-invoice-checked", value: "in_…", ttl_seconds: 86400})
kv_get({key: "last-invoice-checked"})
```

Values up to 256 KB, keys up to 512 bytes, expiry up to 365 days.

## Task state

The state of a piece of work, saved by one session and loaded by another:

```text
state_save({task_id: "refund-1042", state: {order: 1042, step: "waiting"}, note: "Waiting for approval"})
state_load({task_id: "refund-1042"})
```

Up to 256 KB per task. Use the `task_id` as `state_ref` when you ask a person, so the answer leads back to the work: see [Your agent asks permission on Telegram](/guides/telegram-approvals.md).

## Files

Small files go inline, up to 1 MB:

```text
file_put({path: "reports/2026-10.csv", content_base64: "…", content_type: "text/csv"})
```

Larger files, up to 100 MB, go through an upload URL: `file_put({path, upload: {size, content_type}})` returns the URL to `PUT` the bytes to, then `file_put({path, complete: "<upload id>"})` finishes the upload.

Every file has a `disk://…` reference that other tools accept, for example `email_send` attachments.

## Share a file

```text
file_url({path: "reports/2026-10.csv", expires_in: 3600})
```

returns a download link valid for one hour by default, up to 7 days. `file_url` goes through your policy like `email_send`: a rule on tool `file_url` can require a person's approval before a link leaves the project.

## Limits

1 GB per project across key-value, task state, files and inbound email payloads, and up to 10,000 key-value entries, task state included. Files and key-value entries never expire unless you set a `ttl_seconds` on a key; inbound event payloads are deleted after 30 days.
