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

# Attachments

> Read attachment metadata on inbound mail, mint short-lived download links, or fetch the raw bytes.

Files on inbound mail arrive as metadata — filename, content type, size — never as inline bytes. When you need the file, mint a short-lived signed link you can hand to a browser or another service, or download the bytes directly. Every attachment read uses the `messages:read` scope.

<Warning>
  **Treat every inbound attachment as untrusted.** Received files come from arbitrary senders and can carry malware, exploits, or phishing content. A Dairo-hosted download link only means Dairo serves the link — it is not a safety judgment about the file. Never auto-open, auto-execute, or feed attachment bytes into a shell, parser, or renderer. Scan with antivirus, enforce content-type and size limits, process in a sandbox, and require human review before opening files you didn't create.
</Warning>

## Read attachment metadata

Attachment ids come from the `attachments` array on a fetched [message](/receiving/messages-and-threads). Fetch one to get its filename, content type, size, and the message it belongs to.

<CodeGroup>
  ```bash title="cURL" theme={null}
  curl https://api.dairo.app/v1/attachments/f3b1d2a4-6c78-49e0-8b5f-0a1c2d3e4f56 \
    -H "Authorization: Bearer $DAIRO_API_KEY"
  ```

  ```ts title="TypeScript" theme={null}
  const att = await dairo.attachments.get("f3b1d2a4-6c78-49e0-8b5f-0a1c2d3e4f56");
  console.log(att.filename, att.contentType, att.sizeBytes);
  ```

  ```python title="Python" theme={null}
  att = dairo.attachments.get("f3b1d2a4-6c78-49e0-8b5f-0a1c2d3e4f56")
  print(att.filename, att.content_type, att.size_bytes)
  ```
</CodeGroup>

```json theme={null}
{
  "object": "attachment",
  "id": "f3b1d2a4-6c78-49e0-8b5f-0a1c2d3e4f56",
  "messageId": "8a2fdc3b-51e2-4f6a-9c11-2b7e4d9a6f3c",
  "filename": "damage-photo.jpg",
  "contentType": "image/jpeg",
  "sizeBytes": 182034,
  "contentId": null,
  "disposition": "attachment",
  "createdAt": "2026-07-10T14:32:08+00:00"
}
```

The `disposition` is `attachment` for a normal file or `inline` for one referenced from the HTML body (an embedded image, say).

## Get the file

Three endpoints serve the file, each for a different consumer:

| Endpoint                            | What you get                                                              |
| ----------------------------------- | ------------------------------------------------------------------------- |
| `GET /v1/attachments/{id}/url`      | A signed `downloadUrl` plus a human-friendly `shareUrl`, both time-boxed. |
| `GET /v1/attachments/{id}/link`     | The same response shape, always minted as Dairo-branded links.            |
| `GET /v1/attachments/{id}/download` | The raw bytes, served with the file's content type and filename.          |

On `/url` and `/link`, `expiryHours` sets how long the links stay valid — an integer from 1 to 168 (one week). Omit it and the links last five minutes.

<CodeGroup>
  ```bash title="cURL" theme={null}
  curl "https://api.dairo.app/v1/attachments/f3b1d2a4-6c78-49e0-8b5f-0a1c2d3e4f56/url?expiryHours=1" \
    -H "Authorization: Bearer $DAIRO_API_KEY"

  # Or fetch the bytes straight to disk
  curl -o damage-photo.jpg \
    https://api.dairo.app/v1/attachments/f3b1d2a4-6c78-49e0-8b5f-0a1c2d3e4f56/download \
    -H "Authorization: Bearer $DAIRO_API_KEY"
  ```

  ```ts title="TypeScript" theme={null}
  // A signed URL for each attachment on a message
  const message = await dairo.messages.get("8a2fdc3b-51e2-4f6a-9c11-2b7e4d9a6f3c");
  for (const att of message.attachments ?? []) {
    const { downloadUrl, shareUrl } = await dairo.attachments.getUrl(att.id, { expiryHours: 1 });
    console.log(att.filename, downloadUrl, shareUrl);
  }

  // Or the bytes directly
  const bytes = await dairo.attachments.download("f3b1d2a4-6c78-49e0-8b5f-0a1c2d3e4f56");
  ```

  ```python title="Python" theme={null}
  result = dairo.attachments.get_url("f3b1d2a4-6c78-49e0-8b5f-0a1c2d3e4f56", expiry_hours=1)
  print(result.download_url, result.share_url)

  raw = dairo.attachments.download("f3b1d2a4-6c78-49e0-8b5f-0a1c2d3e4f56")
  ```

  ```bash title="CLI" theme={null}
  dairo attachments url f3b1d2a4-6c78-49e0-8b5f-0a1c2d3e4f56 --expiry-hours 1
  dairo messages download-attachments 8a2fdc3b-51e2-4f6a-9c11-2b7e4d9a6f3c --out ./downloads
  ```
</CodeGroup>

```json theme={null}
{
  "attachment": {
    "id": "f3b1d2a4-6c78-49e0-8b5f-0a1c2d3e4f56",
    "messageId": "8a2fdc3b-51e2-4f6a-9c11-2b7e4d9a6f3c",
    "filename": "damage-photo.jpg",
    "contentType": "image/jpeg",
    "sizeBytes": 182034
  },
  "downloadUrl": "https://storage.dairo.app/d/dl.MC4xNGE2…",
  "shareUrl": "https://storage.dairo.app/s/dl.MC4xNGE2…",
  "expiresInSeconds": 3600
}
```

Dairo scans inbound files. Your own authenticated reads work while a scan is still running, but a file the scan flags is withheld — requests for it return `403`.

## Share a file by link

The `shareUrl` is a human-friendly download page — handy when you want to hand a file to someone in an email. Dairo never inserts links into your mail for you: mint the link, then place it in your `text` or `html` deliberately.

Two things to keep in mind when sharing inbound files onward:

* A share link only starts serving an inbound file after it passes Dairo's malware scan; until then the page withholds the download.
* Sharing a file onward means vouching for a file you didn't author. The page is hosted by Dairo, but the file behind it is whatever the sender attached — confirm it's safe before distributing the link.

## Next steps

* [Send an email](/sending/sending-email) — attach files to outbound mail.
* [Share links](/storage/share-links) — the same short-lived links over files in your own storage buckets.
* [API reference](/api-reference) — every endpoint, with copy-paste requests.
