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

# Media without Meta tokens

> Upload once, use a Sendlyy media ID, and download incoming attachments directly.

This contract is introduced with the full-content webhook release (source migration 032). It is not available on the deployed migration-021 backend until the compatible schema/API rollout is completed. The HTTP API remains `/api/v1`, with one full-content subscriber envelope.

### Where does mediaId come from?

A `mediaId` is an opaque **Sendlyy UUID**, not Meta's numeric media ID and not an access token.

* **A customer sends a file:** the full-content webhook contains `message.audio.mediaId`, `message.image.mediaId`, `message.document.mediaId`, etc. Sendlyy creates that reference automatically.
* **Your application uploads a file:** `POST /api/v1/media` returns `data.mediaId`.
* **Download a file:** call `GET /api/v1/media/{mediaId}` with your scoped Sendlyy API key. The response is the file itself, not a temporary URL or a JSON envelope.

Sendlyy handles the Meta lookup, short-lived download URL and access token. You do not need to implement Meta's two-step download protocol. Files remain on WhatsApp's media infrastructure; Sendlyy stores scoped references and encrypted filenames, not a permanent file archive.

### Upload

Grant the key `media:write` and the business number. Its project and environment are inferred from the key. If exactly one number is granted, that number is inferred too:

```bash theme={null}
curl --request POST 'https://api.sendlyy.app/api/v1/media' \
  --header "Authorization: Bearer $SENDLYY_API_KEY" \
  --form 'file=@./invoice.pdf;type=application/pdf'
```

If the key grants multiple numbers, add `--form 'senderId=<sender-uuid>'`. Dashboard sessions must supply `projectId`, `environmentId` and `senderId`. Explicit IDs must always match the key's own grants; they cannot override them. Let your HTTP client set the multipart boundary; do not manually set a JSON Content-Type.

```json theme={null}
{
  "message": "Media uploaded.",
  "data": {
    "mediaId": "6574ca83-d2ef-4a89-9f92-8f80572c2192",
    "mimeType": "application/pdf",
    "filename": "invoice.pdf",
    "size": 18420,
    "expiresAt": "2026-12-01T12:00:00Z"
  }
}
```

Uploads require a Live environment and an eligible connected/rented number. Sandbox never uploads a real file to Meta. An upload stores a file reference; it does not send a WhatsApp message. Use the returned ID in a typed outgoing message, or an approved template header/carousel card. See [messaging](https://docs.sendlyy.app/guides/messaging.md). Media references cannot be substituted into plain text variables to create attachments.

| Kind | MIME types | Upload limit |
| - | - | - |
| Image | `image/jpeg`, `image/png` | 5 MiB |
| Audio | `audio/aac`, `audio/amr`, `audio/mpeg`, `audio/mp4`, `audio/ogg` | 16 MiB |
| Video | `video/mp4`, `video/3gpp` | 16 MiB |
| Document | PDF, plain text, Word, Excel and PowerPoint MIME types, including their Open XML formats | 100 MiB |
| Sticker | `image/webp` | 500 KiB maximum; Meta additionally enforces static/animated sticker constraints |

Meta validates actual format/encoding; declaring a MIME type does not convert a file. Avoid blind upload retries after an uncertain provider response; they may create another provider file. Uploads do not use the message-send charging path.

### Download

Grant the key `media:read` and the corresponding number:

```bash theme={null}
curl --fail 'https://api.sendlyy.app/api/v1/media/6574ca83-d2ef-4a89-9f92-8f80572c2192' \
  --header "Authorization: Bearer $SENDLYY_API_KEY" \
  --output invoice.pdf
```

The response uses attachment disposition, `application/octet-stream`, `Cache-Control: no-store` and `X-Content-Type-Options: nosniff`. The webhook or upload response supplies the original MIME type. Do not put API keys in file URLs or expose them in browser code.

References are scoped to organization, project, environment and number. Key expiry/revocation, missing grants, a disconnected sender or an expired/released rental prevents access. A reference from an older rental allocation cannot be used by the new renter. Media IDs cannot be reused across projects/environments by changing request fields.

References expire at most 30 days after the message/upload timestamp. Meta may remove or make media unavailable sooner; this is not a 30-day storage guarantee. Download and retain important files in your own authorized storage. Expired references return `410`; unknown/inaccessible references return `404` or a permission error. Other provider failures can return `502` without exposing provider credentials or response bodies.

The default download transport allows supported Meta HTTPS hosts, pins public DNS addresses, disables redirects and preserves TLS verification. Generic forward-proxy environments require a reviewed SSRF-safe transport before downloads can be enabled there.


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