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

# Media

> Download media files received from users — images, audio, documents, videos, and stickers.

<Note>
  The media download endpoint is under active development and will be available soon. This page will be updated when the feature is released.
</Note>

When a user sends you an image, voice note, document, or video, the webhook payload contains a `media_id` rather than the file itself. The Connexease Gateway provides a proxy endpoint that handles Meta authentication on your behalf — you call it with your API key and receive the binary file directly.

***

## How It Works

```mermaid theme={null}
sequenceDiagram
    participant U as User (WhatsApp)
    participant G as Connexease Gateway
    participant Y as Your Server

    U->>G: Sends image / audio / document
    G->>Y: Webhook with media_id
    Y->>G: GET /v1/wa/media/{media_id}?phone_number_id=...
    G-->>Y: Binary file content
```

***

## Endpoint

```text theme={null}
GET https://api.gateway.connexease.com/v1/wa/media/{media_id}
```

<ParamField header="Authorization" type="string" required>
  Your API key in `Bearer pk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx` format.
</ParamField>

<ParamField path="media_id" type="string" required>
  The media ID from an incoming message webhook — e.g. `image.id`, `audio.id`, `document.id`.
</ParamField>

<ParamField query="phone_number_id" type="string" required>
  The Meta phone number ID that received the message. Found in `value.metadata.phone_number_id` of the incoming webhook payload. Used to look up the correct WABA credentials.
</ParamField>

**Response:** Binary file content with the appropriate `Content-Type` header (e.g. `image/jpeg`, `audio/ogg`, `application/pdf`).

***

## Example

<CodeGroup>
  ```javascript Node.js theme={null}
  async function downloadMedia(mediaId, phoneNumberId) {
    const res = await fetch(
      `https://api.gateway.connexease.com/v1/wa/media/${mediaId}?phone_number_id=${phoneNumberId}`,
      {
        headers: { 'Authorization': `Bearer ${process.env.CONNEXEASE_API_KEY}` }
      }
    );

    if (!res.ok) {
      const err = await res.json();
      throw new Error(`${err.errors.code}: ${err.errors.description}`);
    }

    const mimeType = res.headers.get('content-type');
    const buffer = Buffer.from(await res.arrayBuffer());
    return { buffer, mimeType };
  }

  // Usage inside your webhook handler:
  setImmediate(async () => {
    const value = body?.entry?.[0]?.changes?.[0]?.value;
    const msg = value?.messages?.[0];
    const phoneNumberId = value?.metadata?.phone_number_id;

    if (msg?.type === 'image') {
      const { buffer, mimeType } = await downloadMedia(msg.image.id, phoneNumberId);
      // Save to S3, disk, database, etc.
    }
  });
  ```

  ```python Python theme={null}
  import os, requests

  API_KEY = os.environ["CONNEXEASE_API_KEY"]

  def download_media(media_id: str, phone_number_id: str) -> dict:
      res = requests.get(
          f"https://api.gateway.connexease.com/v1/wa/media/{media_id}",
          params={"phone_number_id": phone_number_id},
          headers={"Authorization": f"Bearer {API_KEY}"}
      )
      res.raise_for_status()
      return {
          "content": res.content,
          "mime_type": res.headers.get("content-type"),
      }

  # Usage inside your webhook handler:
  def process_webhook(body):
      value = body.get("entry", [{}])[0].get("changes", [{}])[0].get("value", {})
      msg = (value.get("messages") or [{}])[0]
      phone_number_id = value.get("metadata", {}).get("phone_number_id")

      if msg.get("type") == "image":
          result = download_media(msg["image"]["id"], phone_number_id)
          # Save result["content"] to storage
  ```

  ```go Go theme={null}
  func downloadMedia(mediaID, phoneNumberID, apiKey string) ([]byte, string, error) {
      url := fmt.Sprintf(
          "https://api.gateway.connexease.com/v1/wa/media/%s?phone_number_id=%s",
          mediaID, phoneNumberID,
      )
      req, _ := http.NewRequest("GET", url, nil)
      req.Header.Set("Authorization", "Bearer "+apiKey)

      resp, err := http.DefaultClient.Do(req)
      if err != nil { return nil, "", err }
      defer resp.Body.Close()

      content, err := io.ReadAll(resp.Body)
      return content, resp.Header.Get("Content-Type"), err
  }
  ```
</CodeGroup>

***

## Where to Find `phone_number_id`

The `phone_number_id` is in every incoming message webhook under `value.metadata`:

```json theme={null}
{
  "entry": [{
    "changes": [{
      "value": {
        "metadata": {
          "phone_number_id": "PHONE_NUMBER_ID_123",
          "display_phone_number": "905321234567"
        },
        "messages": [{ "type": "image", "image": { "id": "MEDIA_ID_789" } }]
      }
    }]
  }]
}
```

Store the `phone_number_id` alongside the `media_id` when you receive the webhook so you have both values ready when you call the media endpoint.

***

## Media ID Expiry

<Warning>
  Media IDs expire after **30 days**. Download and store the file on your own infrastructure promptly after receiving the webhook.
</Warning>

***

## Supported Media Types

| Type     | Formats                                   | Max Size |
| -------- | ----------------------------------------- | -------- |
| Image    | JPEG, PNG                                 | 5 MB     |
| Audio    | MP3, AAC, AMR, OGG (Opus)                 | 16 MB    |
| Document | PDF, DOC, DOCX, XLS, XLSX, PPT, PPTX, TXT | 100 MB   |
| Video    | MP4, 3GPP                                 | 16 MB    |
| Sticker  | WebP                                      | 500 KB   |

***

## Tips

<Tip>
  Call this endpoint asynchronously — never block your webhook handler while downloading. Return HTTP 200 immediately and process media in a background job.
</Tip>

<Note>
  Media downloads do not count against your message send rate limit (80 req/s). A separate quota applies.
</Note>
