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

# Files and links

> Upload slots, attachments, reading and previewing files, downloads and links — how a hug's page handles files without network access.

Hugs

A hug's page has no network. Files go **in** as attachments, come **back** through upload slots, and everything is
read, uploaded and downloaded through the host, with the viewer's permissions.

| | Who decides | Where |
| - | - | - |
| **Attachments** — run files the page may show | the agent, when it asks | `ctx.attach` → `attachments` |
| **Upload slots** — answer keys that may hold files | the definition | `file()` fields → the Hug's `files` |

***

## Attachments: files the page shows

The agent uploads workspace files and passes them with the ask:

```ts theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
const attachments = await ctx.attach(['documents/*', { path: 'report.pdf', label: 'Final report' }]);
await ctx.ask('review-batch', {
    title, attachments,
    params: { documents: attachments.map((a) => ({ id: a.name, name: a.name, artifact_id: a.artifact_id })) },
});
```

* `ctx.attach(paths)` takes paths or globs inside the workspace, with an optional `label` — at most 25 files of
  25 MB each. A file that can't be attached (no match, too large, outside the workspace) is logged and left out.
* Each attachment is `{ artifact_id, name, size, content_type, sha256 }`. Pass the `artifact_id`s in the params, so
  the page knows which file is which.
* On a resume (`wait: 'yield'`), don't attach again: `ctx.hug.resumed().length > 0`.

**In the page**, `hug.attachments` lists them:

```ts theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
const file = hug.attachments.find((a) => a.artifact_id === doc.artifact_id);
html`<cliq-attachment .ref=${file}></cliq-attachment>`
```

To use the bytes yourself:

```ts theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
const blob = await hug.read_file(file);         // a Blob
const url = await hug.file_url(file);           // an object URL — for <img src>, <iframe src>, <a href>
```

[`compare-versions`](https://github.com/getcliqio/cliq/tree/main/examples/hugs/compare-versions) reads two text files this way and shows images with `file_url`;
[`image-markup`](https://github.com/getcliqio/cliq/tree/main/examples/hugs/image-markup) draws on an image loaded with `file_url`.

`read_file` works for an attachment, the viewer's own uploads, and — in view mode — the files in the answer being
shown. At most 10 reads at once and 50 MB per page.

## Upload slots: files people send

A `file()` field in the definition is an upload slot:

```ts theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
values: z.object({
    evidence: file({ accept: ['image/*', '.pdf'], max_bytes: 20_000_000, optional: true }),
    photos: file({ accept: ['image/*'], multiple: true }),
}),
```

| Option | |
| - | - |
| `accept` | MIME types (`image/*`, `text/csv`) and extensions (`.pdf`) |
| `max_bytes` | At most the server's limit (`CLIQ_ARTIFACT_MAX_MB`, default 50 MB) |
| `multiple` | Up to 10 files; the value is a list |
| `optional` | The answer may leave it out |

**In the page**, `<cliq-file key="evidence">` does the whole thing. By hand:

```ts theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
const ref = await hug.upload('evidence');   // the host's file picker, then the upload
set('evidence', ref);
```

`upload` rejects with a `HugError` — `upload_cancelled`, `file_too_large`, `file_type`, `upload_failed`. The file is
checked against the slot by the host and again by CliqHub (by declared type, extension and first bytes).

**In the agent**, the answer holds the reference, not the bytes:

```ts theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
const { evidence } = answer.value;
const stream = await ctx.download(evidence);                        // the bytes
const saved = await ctx.download(evidence, 'in/evidence.pdf');     // or saved in the workspace
```

A later phase can save every file people sent with `cliq hug files` ([Asking people](https://docs.getcliq.io/hugs.md#example-2-collect-a-file-in-the-review)).

## Downloads

```ts theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
hug.download(file);
```

The host saves the file for the person — the frame can't download by itself. `<cliq-attachment>` has a Download
button.

## Links

```ts theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
hug.open_link('https://example.com/ticket/123');
```

Opens in a new tab. A URL that appears in the Hug's params opens at once; any other is shown to the person first,
so a page can't send people somewhere unexpected. Only `http` and `https`.


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