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

# Send a media message

> Send an image, video, document, or audio WhatsApp message.

Send a media message by URL or by direct file upload. Requires an open 24-hour session, same as free-form text.

## Endpoint

```
POST /api/v1/{subdomain}/messages/media
```

Required ability: `messages.send`.

## Headers

| Header          | Value                                       |
| --------------- | ------------------------------------------- |
| `Authorization` | `Bearer <your-64-hex-token>`                |
| `Content-Type`  | `application/json` or `multipart/form-data` |
| `Accept`        | `application/json`                          |

<Tip>Use `multipart/form-data` when uploading via `media_file`. Use `application/json` when referencing `media_url`.</Tip>

## Path parameters

<ParamField path="subdomain" type="string" required={true}>
  Your workspace subdomain.
</ParamField>

## Body parameters

<ParamField body="phone_number" type="string" required={true}>
  Recipient phone number. 10–15 characters.
</ParamField>

<ParamField body="media_type" type="string" required={true}>
  One of `image`, `document`, `video`, `audio`. Stickers are **not** supported via API.
</ParamField>

<ParamField body="media_url" type="string" required={false}>
  Public URL of the media asset. Required if `media_file` is not supplied.
</ParamField>

<ParamField body="media_file" type="file" required={false}>
  Uploaded media file. Required if `media_url` is not supplied.
</ParamField>

<ParamField body="caption" type="string" required={false}>
  Caption for `image`, `video`, or `document`. Max 1024 characters.
</ParamField>

<ParamField body="filename" type="string" required={false}>
  Display filename for document messages. Max 255 characters.
</ParamField>

<ParamField body="contact" type="object" required={false}>
  Optional contact payload for creating or updating the contact behind this send.
</ParamField>

## Example request

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://dash.xobito.com/api/v1/acme/messages/media \
    -H "Authorization: Bearer <your_token>" \
    -H "Content-Type: application/json" \
    -H "Accept: application/json" \
    -d '{
      "phone_number": "+14155551234",
      "media_type": "image",
      "media_url": "https://example.com/invoice.png",
      "caption": "Your invoice"
    }'
  ```

  ```javascript JavaScript theme={null}
  const res = await fetch(
    "https://dash.xobito.com/api/v1/acme/messages/media",
    {
      method: "POST",
      headers: {
        Authorization: "Bearer <your_token>",
        "Content-Type": "application/json",
        Accept: "application/json",
      },
      body: JSON.stringify({
        phone_number: "+14155551234",
        media_type: "image",
        media_url: "https://example.com/invoice.png",
        caption: "Your invoice",
      }),
    }
  );
  const body = await res.json();
  ```

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

  r = requests.post(
      "https://dash.xobito.com/api/v1/acme/messages/media",
      headers={
          "Authorization": "Bearer <your_token>",
          "Accept": "application/json",
      },
      json={
          "phone_number": "+14155551234",
          "media_type": "image",
          "media_url": "https://example.com/invoice.png",
          "caption": "Your invoice",
      },
  )
  body = r.json()
  ```

  ```php PHP theme={null}
  <?php
  $ch = curl_init('https://dash.xobito.com/api/v1/acme/messages/media');
  curl_setopt_array($ch, [
      CURLOPT_POST => true,
      CURLOPT_RETURNTRANSFER => true,
      CURLOPT_HTTPHEADER => [
          'Authorization: Bearer <your_token>',
          'Content-Type: application/json',
          'Accept: application/json',
      ],
      CURLOPT_POSTFIELDS => json_encode([
          'phone_number' => '+14155551234',
          'media_type'   => 'image',
          'media_url'    => 'https://example.com/invoice.png',
          'caption'      => 'Your invoice',
      ]),
  ]);
  $body = json_decode(curl_exec($ch), true);
  curl_close($ch);
  ```
</CodeGroup>

## Example response

```json 200 OK theme={null}
{
  "status": "success",
  "message": "Media message sent successfully",
  "data": {
    "message_id": "wamid.HBgLMTQxNTU1NTEyMzQVAgARGBI5QUQ1N...",
    "contact_id": 451,
    "phone": "+14155551234",
    "status": "sent",
    "sent_at": "2026-04-16T12:00:00.000000Z",
    "chat_id": 88,
    "contact_created": false
  }
}
```

## Error responses

| Status | When                    | Example body                                                                                                                                  |
| ------ | ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| `401`  | Missing / invalid token | `{"status":"error","message":"Invalid API token"}`                                                                                            |
| `403`  | Missing ability         | `{"status":"error","message":"Token does not have the required ability: messages.send"}`                                                      |
| `422`  | Validation              | `{"status":"error","message":"Validation failed","errors":{"media_url":["The media url field is required when media file is not present."]}}` |
| `429`  | Rate limit              | `{"message":"Too many requests","retry_after":45}`                                                                                            |
| `500`  | Send failure            | `{"status":"error","message":"Failed to send media message"}`                                                                                 |
