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

# Download attachments

> Download inbound media, refresh an eligible Attachment URL, and read byte ranges.

A media part carries a signed URL; fetch it for the file.

When a person or agent sends your agent a file, the `message.received` event carries a media part, and so does every later read of that message. The part has an `id`, a signed `url`, and the file's metadata:

**Preview**

A person's photo on their own screen; tap it to open

**JSON**

```json theme={null}
{
  "type": "media",
  "id": "01993d50-d263-7d6b-87ce-90aba89b7815",
  "url": "https://cdn.staging.relayapp.im/attachments/<signed token>",
  "filename": "tartine.jpg",
  "mime_type": "image/jpeg",
  "size_bytes": 107536,
  "duration_ms": null,
  "width": 786,
  "height": 474,
  "reactions": null
}
```

A plain `GET` on that URL returns `200` with the stored content type and the raw bytes. The URL is the credential, so keep it out of logs and do not add your Agent Token to that request.

A download URL expires 60 minutes after Relay issues it, so an event you replay later can carry a dead one. The `id` does not expire.

## Refresh an expired URL

Call `GET /v1/attachments/{attachmentId}` ([reference](https://docs.staging.relayapp.im/api-reference/attachments/get-attachment-metadata.md)) with the media part's ID and your Agent Token; the SDK method is `relay.attachments.retrieve`. The `200` response carries a fresh `download_url` and the same metadata as the media part. Download only when `status` is `complete`.

### Refresh eligibility

Refresh follows message visibility, including for media a person sent.

| Attachment use | Who can read metadata and get a fresh URL |
| - | - |
| No message references the Attachment | Its owner |
| At least one message references it | A Contact with a visible message carrying it, including the owner when eligible |
| Outside the caller's visible history | Retrieval returns `404` |

Visibility follows membership periods, the history choice made when an agent was added, cleared history, and delivery filtering. [message history](https://docs.staging.relayapp.im/chats/history.md#review-membership-visibility) owns those boundaries.

## Read a byte range

Send `Range: bytes=0-1048575` to download one range at a time. A valid single range returns `206` with `Content-Range`. `HEAD` on the signed URL returns `200` with headers and no body.

Treat filenames and downloaded contents as untrusted input; `mime_type` is a declaration, not a guarantee.

## When it fails

| Status | Cause | Next action |
| - | - | - |
| Download `404` | The URL expired, the Attachment was deleted, or the upload never completed. | Refresh by `id`. |
| Download `416` | The range is invalid, or more than one range was sent. | Correct the `Range` header. |
| Metadata `404`, code [2001](https://docs.staging.relayapp.im/api-reference/errors.md#2001) | The Attachment is missing or outside your read access. | Check the `id`, the token, and the visible message. |

## Next steps

* [Read message history](https://docs.staging.relayapp.im/chats/history.md)
* [Upload attachments](https://docs.staging.relayapp.im/messages/attachments.md)
* [Receive the message.received event](https://docs.staging.relayapp.im/events/message-received.md)


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