# Work with attachments

Download received files for your agent to read, or attach a file to a message you send.

Source: https://cherami.to/docs/guides/attachments



Download files you receive to read with your own tools, or attach files to mail you send.

## Download a received file [#download-a-received-file]

[Fetch the message](https://cherami.to/docs/guides/receiving#read-a-message) and, once it's `ready`, pick a file from `content.attachments` and download it by its `id`.

With MCP, call `read_attachment` with `message_id` and `attachment_id`. It returns the file as base64 chunks: start at `offset` 0 and continue with the returned `next_offset` until it's null, decoding each chunk and joining them in order.

With HTTP, set `MESSAGE_ID`, `ATTACHMENT_ID`, `DOWNLOAD_PATH` and `CHERAMI_API_KEY` ([get a credential](https://cherami.to/docs/quickstart#get-a-credential)):

```bash
curl --fail --silent --show-error \
  "https://cherami.to/v1/messages/$MESSAGE_ID/attachments/$ATTACHMENT_ID" \
  -H "Authorization: Bearer $CHERAMI_API_KEY" \
  --output "$DOWNLOAD_PATH"
```

If the message failed processing, [download the original email](https://cherami.to/docs/api/messages/download-raw-message) and use your own MIME tools.

Need to extract information from invoices, receipts, reports, spreadsheets or presentations? Email [hello@cherami.to](mailto:hello@cherami.to) and we can talk about your workflow.

### Download with TypeScript [#download-with-typescript]

After [SDK setup](https://cherami.to/docs/typescript#install-and-connect):

```ts
import { writeFile } from "node:fs/promises";

const { data: file } = await client.downloadAttachment({
  message_id: messageId,
  attachment_id: attachmentId,
});
await writeFile(outputPath, new Uint8Array(await file.arrayBuffer()));
```

`data` is a standard `Response`. `client.downloadRawMessage({ message_id: messageId })` returns the original email the same way.

### Download with Python [#download-with-python]

After [Python SDK setup](https://cherami.to/docs/python#install-and-connect):

```python
import os
from cherami import Cherami

with Cherami(os.environ["CHERAMI_API_KEY"]) as client:
    response = client.download_attachment({
        "message_id": message_id, "attachment_id": attachment_id,
    }).data
    try:
        with open(output_path, "wb") as file:
            for chunk in response.iter_bytes():
                file.write(chunk)
    finally:
        response.close()
```

With an async client, await `client.download_attachment`, read with `async for` over `response.aiter_bytes()` and close with `await response.aclose()`. `download_raw_message` returns the original email the same way.

## Attach a file to a send [#attach-a-file-to-a-send]

Add an `attachments` array to your [send request](https://cherami.to/docs/guides/sending#send-a-message), with each file's name, MIME type and base64-encoded bytes (padded, with no whitespace). This attaches a text file containing `Hello`:

```json
{
  "attachments": [
    {"filename": "notes.txt", "type": "text/plain", "content": "SGVsbG8="}
  ]
}
```

A message, attachments included, can be up to 5 MiB after base64 encoding, which adds about a third, so about 3.5 MiB of files fit. A larger message is refused with `413 message_too_large`.

With TypeScript, `attachment` encodes the bytes for you; it also accepts an ArrayBuffer or Blob:

```ts
import { readFile } from "node:fs/promises";
import { attachment } from "@cherami/sdk";

const file = await attachment("notes.pdf", await readFile(localPath), "application/pdf");
// Use attachments: [file] in the body passed to prepareSend.
```

With Python, `attachment` accepts bytes, bytearray or memoryview:

```python
from pathlib import Path
from cherami import attachment

file = attachment("notes.pdf", Path(local_path).read_bytes(), "application/pdf")
# Use "attachments": [file] in the body passed to prepare_send.
```

To pass on a file you received, [forward the message](https://cherami.to/docs/guides/sending#forward-a-message) instead: a forward includes the original attachments.

## Retrieve sent attachments [#retrieve-sent-attachments]

`GET /v1/sent/MESSAGE_ID` returns each file's base64 `content` in `submission.attachments`; with MCP, call `get_sent_message` with `format: "full"`. Conversation detail lists attachments without their bytes.

To decode a file from a sent message or draft, use `attachmentBytes(savedAttachment)` from `@cherami/sdk`, which returns a `Uint8Array`, or `attachment_bytes(saved_attachment)` from `cherami`, which returns `bytes`.

[Attachment fields and validation](https://cherami.to/docs/api/sending/send-message)
