> ## 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.

# Attachments

> Allocate, upload, send, download, and delete Relay Attachments.

Pre-upload files when you need trusted ownership, metadata, reuse, or files larger than 10 MB.

## 1. Create an upload

<CodeGroup>
  ```typescript TypeScript SDK theme={null}
  const allocation = await relay.attachments.create({
    filename: "report.pdf",
    content_type: "application/pdf",
    size_bytes: bytes.byteLength,
  });
  ```

  ```bash cURL theme={null}
  curl -sS -X POST https://api.relayapp.im/v1/attachments \
    -H "Authorization: Bearer $RELAY_AGENT_TOKEN" \
    -H "Content-Type: application/json" \
    -d '{
      "filename":"report.pdf",
      "content_type":"application/pdf",
      "size_bytes":48291
    }'
  ```
</CodeGroup>

The response contains an `attachment_id`, signed upload URL, HTTP method, exact required headers, and expiration time.

```json theme={null}
{
  "attachment_id":"01993d50-d263-7d6b-87ce-90aba89b7815",
  "upload_url":"https://api.relayapp.im/attachment-transfers/<token>",
  "download_url":"https://api.relayapp.im/attachment-transfers/<token>",
  "http_method":"PUT",
  "expires_at":"2026-08-29T06:35:00.000Z",
  "required_headers":{
    "Content-Type":"application/pdf",
    "Content-Length":"48291"
  }
}
```

## 2. Upload the raw bytes

<CodeGroup>
  ```typescript TypeScript SDK theme={null}
  await relay.attachments.upload(allocation, bytes);
  ```

  ```bash cURL theme={null}
  curl -sS -X PUT "$UPLOAD_URL" \
    -H "Content-Type: application/pdf" \
    -H "Content-Length: 48291" \
    --data-binary @report.pdf
  ```
</CodeGroup>

Do not use multipart form data. The body length and Content-Type must match the allocation.

## 3. Send the Attachment

<CodeGroup>
  ```typescript TypeScript SDK theme={null}
  await relay.chats.messages.send(chatId, {
    message: {
      parts: [{
        type: "media",
        attachment_id: allocation.attachment_id,
      }],
    },
  });
  ```

  ```bash cURL theme={null}
  curl -sS -X POST "https://api.relayapp.im/v1/chats/$CHAT_ID/messages" \
    -H "Authorization: Bearer $RELAY_AGENT_TOKEN" \
    -H "Content-Type: application/json" \
    -d '{
      "message":{
        "parts":[{
          "type":"media",
          "attachment_id":"01993d50-d263-7d6b-87ce-90aba89b7815"
        }]
      }
    }'
  ```
</CodeGroup>

## Import a public media URL

For files up to 10 MB, a `media` part can use a public HTTPS URL instead of a
pre-uploaded Attachment:

<CodeGroup>
  ```typescript TypeScript SDK theme={null}
  await relay.chats.messages.send(chatId, {
    message: {
      parts: [{
        type: "media",
        url: "https://cdn.yourdomain.com/report.pdf",
      }],
    },
  });
  ```

  ```bash cURL theme={null}
  curl -sS -X POST "https://api.relayapp.im/v1/chats/$CHAT_ID/messages" \
    -H "Authorization: Bearer $RELAY_AGENT_TOKEN" \
    -H "Content-Type: application/json" \
    -d '{
      "message":{
        "parts":[{
          "type":"media",
          "url":"https://cdn.yourdomain.com/report.pdf"
        }]
      }
    }'
  ```
</CodeGroup>

Relay imports the bytes into an owned Attachment before committing the
Message.

| Check        | Rule                                                  |
| ------------ | ----------------------------------------------------- |
| Network      | Every DNS answer must be on the public network        |
| Redirects    | Every hop is revalidated, with at most five redirects |
| Response     | Final response must succeed                           |
| Size         | Declared and actual body must each be 1 byte to 10 MB |
| Content type | Response Content-Type must be supported               |

**Relay rejects credentials in the URL and blocks loopback, private,
link-local, reserved, and internal destinations.** A redirect cannot bypass
these checks.

## Media metadata

| Field             | Applies to       |
| ----------------- | ---------------- |
| `duration_ms`     | Audio and video  |
| `width`, `height` | Image and video  |
| `filename`        | Every Attachment |
| `content_type`    | Every Attachment |
| `size_bytes`      | Every Attachment |

Width and height must be supplied together.

## Image formats

Relay accepts raster image formats including JPEG, PNG, GIF, HEIC, TIFF, BMP,
and WebP. Relay rejects SVG.

<Info>
  This is a Relay content-safety decision. Reference material disagreed about
  SVG support, so Relay chose one explicit rule: accept WebP raster bytes and
  avoid SVG's additional active-content and renderer surface.
</Info>

## Limits

| Boundary                           |            Value |
| ---------------------------------- | ---------------: |
| Allocation size                    | 1 byte to 100 MB |
| Public HTTPS URL import            |      Up to 10 MB |
| Public URL media parts per Message |               40 |
| Total parts per Message            |              100 |

## Ownership

Only the Contact that allocated an Attachment can retrieve or send it. An
Attachment can be reused across Messages, but it cannot be deleted after a
Message or group photo references it. Signed transfer URLs are capabilities;
keep them out of logs.

## Related

* [Voice memos](/guides/messaging/voice-memos)
* [Sending Messages](/guides/messaging/sending-messages)
