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

# Send Media

> Send an image, video, audio (voice/regular) or document by URL or base64

**Auth:** `TokenAccount` or `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotent:** no

## Description

Sends a media file (`image`, `video`, `document` or `audio`) from a **public URL** or a **base64 string**. The source can be provided in `mediaUrl` (which accepts either the URL or the base64 of the media) **or** in the `mediaBase64` field, send one or the other, never both. Supports `message` as a caption, `replyTo` (quote by ID), `replyPrivate`, `mention` / `mentionAll` (group chats only), `delay` (in seconds) to simulate real typing and, for audio, `isVoice` (PTT), `duration` and `waveform`. The server downloads the file (when a URL) or decodes the base64, detects the `mimeType` when omitted and uploads it to the WhatsApp servers before sending.

## Examples

### Image by URL

Sends an image (`mediaType: "image"`) downloaded from a public URL, with `message` used as the caption that appears below the photo in the chat.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST "https://ryzeapi.cloud/api/message/media/$Instance_Name" \
    -H "token: $Token_Instance" \
    -H "Content-Type: application/json" \
    -d '{
      "number":    "5511999999999",
      "mediaType": "image",
      "mediaUrl":  "https://example.com/photo.jpg",
      "message":   "Check out this photo!"
    }'
  ```

  ```javascript JavaScript theme={null}
  await fetch(`https://ryzeapi.cloud/api/message/media/${process.env.Instance_Name}`, {
    method: "POST",
    headers: {
      "token":        process.env.Token_Instance,
      "Content-Type": "application/json"
    },
    body: JSON.stringify({
      number:    "5511999999999",
      mediaType: "image",
      mediaUrl:  "https://example.com/photo.jpg",
      message:   "Check out this photo!"
    })
  });
  ```

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

  requests.post(
      f"https://ryzeapi.cloud/api/message/media/{os.environ['Instance_Name']}",
      headers={
          "token":        os.environ["Token_Instance"],
          "Content-Type": "application/json"
      },
      json={
          "number":    "5511999999999",
          "mediaType": "image",
          "mediaUrl":  "https://example.com/photo.jpg",
          "message":   "Check out this photo!"
      }
  )
  ```

  ```go Go theme={null}
  package main

  import (
      "net/http"
      "os"
      "strings"
  )

  func main() {
      body := strings.NewReader(`{
          "number":    "5511999999999",
          "mediaType": "image",
          "mediaUrl":  "https://example.com/photo.jpg",
          "message":   "Check out this photo!"
      }`)
      req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/message/media/"+os.Getenv("Instance_Name"), body)
      req.Header.Set("token", os.Getenv("Token_Instance"))
      req.Header.Set("Content-Type", "application/json")
      http.DefaultClient.Do(req)
  }
  ```
</CodeGroup>

### Image via base64

Instead of a URL, send the media content as base64. You can put the base64 in `mediaUrl` itself (with or without the `data:image/jpeg;base64,` data URI prefix) **or** in the dedicated `mediaBase64` field. Since there is no URL to infer the name/type, providing `mimeType` (and `fileName`, for documents) is recommended.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST "https://ryzeapi.cloud/api/message/media/$Instance_Name" \
    -H "token: $Token_Instance" \
    -H "Content-Type: application/json" \
    -d '{
      "number":      "5511999999999",
      "mediaType":   "image",
      "mediaBase64": "/9j/4AAQSkZJRgABAQAAAQABAAD...",
      "mimeType":    "image/jpeg",
      "message":     "Check out this photo!"
    }'
  ```

  ```javascript JavaScript theme={null}
  await fetch(`https://ryzeapi.cloud/api/message/media/${process.env.Instance_Name}`, {
    method: "POST",
    headers: {
      "token":        process.env.Token_Instance,
      "Content-Type": "application/json"
    },
    body: JSON.stringify({
      number:      "5511999999999",
      mediaType:   "image",
      mediaBase64: "/9j/4AAQSkZJRgABAQAAAQABAAD...",
      mimeType:    "image/jpeg",
      message:     "Check out this photo!"
    })
  });
  ```

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

  with open("photo.jpg", "rb") as f:
      media_b64 = base64.b64encode(f.read()).decode()

  requests.post(
      f"https://ryzeapi.cloud/api/message/media/{os.environ['Instance_Name']}",
      headers={
          "token":        os.environ["Token_Instance"],
          "Content-Type": "application/json"
      },
      json={
          "number":      "5511999999999",
          "mediaType":   "image",
          "mediaBase64": media_b64,
          "mimeType":    "image/jpeg",
          "message":     "Check out this photo!"
      }
  )
  ```

  ```go Go theme={null}
  package main

  import (
      "net/http"
      "os"
      "strings"
  )

  func main() {
      body := strings.NewReader(`{
          "number":      "5511999999999",
          "mediaType":   "image",
          "mediaBase64": "/9j/4AAQSkZJRgABAQAAAQABAAD...",
          "mimeType":    "image/jpeg",
          "message":     "Check out this photo!"
      }`)
      req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/message/media/"+os.Getenv("Instance_Name"), body)
      req.Header.Set("token", os.Getenv("Token_Instance"))
      req.Header.Set("Content-Type", "application/json")
      http.DefaultClient.Do(req)
  }
  ```
</CodeGroup>

### Voice audio (PTT)

When `mediaType: "audio"` and `isVoice` is omitted, the server defaults to `true` (voice/PTT message). To send as a "regular" audio (a music track, for example), pass `isVoice: false`.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST "https://ryzeapi.cloud/api/message/media/$Instance_Name" \
    -H "token: $Token_Instance" \
    -H "Content-Type: application/json" \
    -d '{
      "number":    "5511999999999",
      "mediaType": "audio",
      "mediaUrl":  "https://example.com/audio.ogg",
      "isVoice":   true
    }'
  ```

  ```javascript JavaScript theme={null}
  await fetch(`https://ryzeapi.cloud/api/message/media/${process.env.Instance_Name}`, {
    method: "POST",
    headers: {
      "token":        process.env.Token_Instance,
      "Content-Type": "application/json"
    },
    body: JSON.stringify({
      number:    "5511999999999",
      mediaType: "audio",
      mediaUrl:  "https://example.com/audio.ogg",
      isVoice:   true
    })
  });
  ```

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

  requests.post(
      f"https://ryzeapi.cloud/api/message/media/{os.environ['Instance_Name']}",
      headers={
          "token":        os.environ["Token_Instance"],
          "Content-Type": "application/json"
      },
      json={
          "number":    "5511999999999",
          "mediaType": "audio",
          "mediaUrl":  "https://example.com/audio.ogg",
          "isVoice":   True
      }
  )
  ```

  ```go Go theme={null}
  package main

  import (
      "net/http"
      "os"
      "strings"
  )

  func main() {
      body := strings.NewReader(`{
          "number":    "5511999999999",
          "mediaType": "audio",
          "mediaUrl":  "https://example.com/audio.ogg",
          "isVoice":   true
      }`)
      req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/message/media/"+os.Getenv("Instance_Name"), body)
      req.Header.Set("token", os.Getenv("Token_Instance"))
      req.Header.Set("Content-Type", "application/json")
      http.DefaultClient.Do(req)
  }
  ```
</CodeGroup>

### Document (PDF) with `fileName`

Sends a PDF as a document. `fileName` (`Contract-2026.pdf`) sets the name displayed in the attachment card and `message` appears as accompanying text. Without `fileName`, WhatsApp shows a generic name.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST "https://ryzeapi.cloud/api/message/media/$Instance_Name" \
    -H "token: $Token_Instance" \
    -H "Content-Type: application/json" \
    -d '{
      "number":    "5511999999999",
      "mediaType": "document",
      "mediaUrl":  "https://example.com/contract.pdf",
      "fileName":  "Contract-2026.pdf",
      "message":   "Here is the attached contract."
    }'
  ```

  ```javascript JavaScript theme={null}
  await fetch(`https://ryzeapi.cloud/api/message/media/${process.env.Instance_Name}`, {
    method: "POST",
    headers: {
      "token":        process.env.Token_Instance,
      "Content-Type": "application/json"
    },
    body: JSON.stringify({
      number:    "5511999999999",
      mediaType: "document",
      mediaUrl:  "https://example.com/contract.pdf",
      fileName:  "Contract-2026.pdf",
      message:   "Here is the attached contract."
    })
  });
  ```

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

  requests.post(
      f"https://ryzeapi.cloud/api/message/media/{os.environ['Instance_Name']}",
      headers={
          "token":        os.environ["Token_Instance"],
          "Content-Type": "application/json"
      },
      json={
          "number":    "5511999999999",
          "mediaType": "document",
          "mediaUrl":  "https://example.com/contract.pdf",
          "fileName":  "Contract-2026.pdf",
          "message":   "Here is the attached contract."
      }
  )
  ```

  ```go Go theme={null}
  package main

  import (
      "net/http"
      "os"
      "strings"
  )

  func main() {
      body := strings.NewReader(`{
          "number":    "5511999999999",
          "mediaType": "document",
          "mediaUrl":  "https://example.com/contract.pdf",
          "fileName":  "Contract-2026.pdf",
          "message":   "Here is the attached contract."
      }`)
      req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/message/media/"+os.Getenv("Instance_Name"), body)
      req.Header.Set("token", os.Getenv("Token_Instance"))
      req.Header.Set("Content-Type", "application/json")
      http.DefaultClient.Do(req)
  }
  ```
</CodeGroup>

### Video with caption and delay

Sends an MP4 video with the caption "Check out this video!" and `delay: 3`, the server sends the "typing..." indicator for 3 seconds before firing the video, simulating real typing.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST "https://ryzeapi.cloud/api/message/media/$Instance_Name" \
    -H "token: $Token_Instance" \
    -H "Content-Type: application/json" \
    -d '{
      "number":    "5511999999999",
      "mediaType": "video",
      "mediaUrl":  "https://example.com/video.mp4",
      "message":   "Check out this video!",
      "delay":     3
    }'
  ```

  ```javascript JavaScript theme={null}
  await fetch(`https://ryzeapi.cloud/api/message/media/${process.env.Instance_Name}`, {
    method: "POST",
    headers: {
      "token":        process.env.Token_Instance,
      "Content-Type": "application/json"
    },
    body: JSON.stringify({
      number:    "5511999999999",
      mediaType: "video",
      mediaUrl:  "https://example.com/video.mp4",
      message:   "Check out this video!",
      delay:     3
    })
  });
  ```

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

  requests.post(
      f"https://ryzeapi.cloud/api/message/media/{os.environ['Instance_Name']}",
      headers={
          "token":        os.environ["Token_Instance"],
          "Content-Type": "application/json"
      },
      json={
          "number":    "5511999999999",
          "mediaType": "video",
          "mediaUrl":  "https://example.com/video.mp4",
          "message":   "Check out this video!",
          "delay":     3
      }
  )
  ```

  ```go Go theme={null}
  package main

  import (
      "net/http"
      "os"
      "strings"
  )

  func main() {
      body := strings.NewReader(`{
          "number":    "5511999999999",
          "mediaType": "video",
          "mediaUrl":  "https://example.com/video.mp4",
          "message":   "Check out this video!",
          "delay":     3
      }`)
      req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/message/media/"+os.Getenv("Instance_Name"), body)
      req.Header.Set("token", os.Getenv("Token_Instance"))
      req.Header.Set("Content-Type", "application/json")
      http.DefaultClient.Do(req)
  }
  ```
</CodeGroup>

### Image in group with reply and mention

Sends an image in a group (`@g.us`), quoting a previous message via `replyTo` and mentioning a member via the `mention` array. `@5511888888888` in the caption becomes clickable and triggers a notification for the tagged user.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST "https://ryzeapi.cloud/api/message/media/$Instance_Name" \
    -H "token: $Token_Instance" \
    -H "Content-Type: application/json" \
    -d '{
      "number":    "120363406289005073@g.us",
      "mediaType": "image",
      "mediaUrl":  "https://example.com/banner.jpg",
      "message":   "Check it out @5511888888888, it is ready!",
      "replyTo":   "3EB08FCF27E532F1B0F5",
      "mention":   ["5511888888888"]
    }'
  ```

  ```javascript JavaScript theme={null}
  await fetch(`https://ryzeapi.cloud/api/message/media/${process.env.Instance_Name}`, {
    method: "POST",
    headers: {
      "token":        process.env.Token_Instance,
      "Content-Type": "application/json"
    },
    body: JSON.stringify({
      number:    "120363406289005073@g.us",
      mediaType: "image",
      mediaUrl:  "https://example.com/banner.jpg",
      message:   "Check it out @5511888888888, it is ready!",
      replyTo:   "3EB08FCF27E532F1B0F5",
      mention:   ["5511888888888"]
    })
  });
  ```

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

  requests.post(
      f"https://ryzeapi.cloud/api/message/media/{os.environ['Instance_Name']}",
      headers={
          "token":        os.environ["Token_Instance"],
          "Content-Type": "application/json"
      },
      json={
          "number":    "120363406289005073@g.us",
          "mediaType": "image",
          "mediaUrl":  "https://example.com/banner.jpg",
          "message":   "Check it out @5511888888888, it is ready!",
          "replyTo":   "3EB08FCF27E532F1B0F5",
          "mention":   ["5511888888888"]
      }
  )
  ```

  ```go Go theme={null}
  package main

  import (
      "net/http"
      "os"
      "strings"
  )

  func main() {
      body := strings.NewReader(`{
          "number":    "120363406289005073@g.us",
          "mediaType": "image",
          "mediaUrl":  "https://example.com/banner.jpg",
          "message":   "Check it out @5511888888888, it is ready!",
          "replyTo":   "3EB08FCF27E532F1B0F5",
          "mention":   ["5511888888888"]
      }`)
      req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/message/media/"+os.Getenv("Instance_Name"), body)
      req.Header.Set("token", os.Getenv("Token_Instance"))
      req.Header.Set("Content-Type", "application/json")
      http.DefaultClient.Do(req)
  }
  ```
</CodeGroup>

## Success response

The `messageType` echoes the `mediaType` you sent (`image`, `video`, `document` or `audio`). The metadata resolved by the upload appears in `mediaUrl` (URL re-issued for `mmg.whatsapp.net`), `mediaMimeType` and `mediaSize`. For PTT audio, the server also returns `mediaDuration` when it can compute it.

```json 200 OK theme={null}
{
  "success": true,
  "message": "Media message sent successfully",
  "status":  "sent",
  "data": {
    "messageId":     "3EB08FCF27E532F1B0F5",
    "direction":     "outgoing",
    "messageType":   "image",
    "content":       "Check out this photo!",
    "source":        "api",
    "timestamp":     "2026-04-30T14:30:00Z",
    "mediaUrl":      "https://mmg.whatsapp.net/v/t62.7118-24/...",
    "mediaMimeType": "image/jpeg",
    "mediaSize":     184320,
    "chat": {
      "jid":     "5511999999999@s.whatsapp.net",
      "isGroup": false
    },
    "sender": {
      "jid":      "5511777777777@s.whatsapp.net",
      "instance": "minha-instancia"
    }
  }
}
```

<Note>
  For `mediaType: "document"`, `fileName` appears in the card. For `mediaType: "audio"` with `isVoice: true`, the message is delivered as PTT (waveform + microphone icon). For "regular" audio (a music track), use `isVoice: false`.
</Note>

## Path parameters

<ParamField path="instance" type="string" required>
  Instance name (e.g., `$Instance_Name`).
</ParamField>

## Headers

<ParamField header="token" type="string" required>
  `TokenAccount` or `TokenInstance`.
</ParamField>

<ParamField header="Content-Type" type="string" required>
  `application/json`
</ParamField>

## Request body

<ParamField body="number" type="string" required>
  Destination: phone (`5511999999999`) or JID (`@s.whatsapp.net`, `@lid`, `@g.us`, `@newsletter`).
</ParamField>

<ParamField body="mediaType" type="string" required>
  One of: `image`, `video`, `document`, `audio`. Determines how WhatsApp renders the message.
</ParamField>

<ParamField body="mediaUrl" type="string">
  Media source: a **public URL** of the file **or** the **base64** of the media (with a `data:<mime>;base64,...` data URI or raw base64). When it is a URL, it must be publicly accessible (no authentication) and the server downloads it. You must provide `mediaUrl` **or** `mediaBase64` (never both).
</ParamField>

<ParamField body="mediaBase64" type="string">
  Base64 of the media (alternative to `mediaUrl`), with a `data:<mime>;base64,...` data URI or raw base64. **Cannot be used together with `mediaUrl`**, send one or the other. Since there is no URL, providing `mimeType` (and `fileName` for documents) is recommended.
</ParamField>

<ParamField body="message" type="string">
  Media caption. For `mediaType: "document"`, it appears as accompanying text. Optional for all types.
</ParamField>

<ParamField body="mimeType" type="string">
  File MIME type (e.g., `image/jpeg`, `application/pdf`). When omitted, the server detects it automatically from the download.
</ParamField>

<ParamField body="fileName" type="string">
  File name displayed in the card. **Recommended for `mediaType: "document"`**, without it, WhatsApp shows a generic name.
</ParamField>

<ParamField body="isVoice" type="boolean" default="true (for audio)">
  Applies only to `mediaType: "audio"`. When `true`, the message is delivered as **PTT** (voice message, with a waveform). When `false`, it is delivered as regular audio (a music track). When the field is omitted on `audio`, the server assumes `true`.
</ParamField>

<ParamField body="duration" type="uint32">
  Audio duration in seconds. Applies only to `mediaType: "audio"`. Optional, when omitted, the server tries to detect it automatically.
</ParamField>

<ParamField body="waveform" type="byte[]">
  Pre-computed audio waveform (PTT). Optional, when omitted, the server automatically generates a default waveform. Applies only to `mediaType: "audio"` with `isVoice: true`.
</ParamField>

<ParamField body="delay" type="int" default="0">
  Time in **seconds** to wait before sending. During the interval, the server sends the "typing..." indicator to the recipient and fires "paused" before the actual send.
</ParamField>

<ParamField body="replyTo" type="string">
  ID of the message to be quoted (reply). The original message must belong to the same instance and have been saved in the database. Possible errors: `reply_message_not_found`, `reply_message_instance_mismatch`.
</ParamField>

<ParamField body="replyPrivate" type="boolean" default="false">
  When `true` **and** `replyTo` points to a message originating from a group, the reply is redirected to the original author's **private** chat (keeping the quote). Ignored if the original message is not from a group.
</ParamField>

<ParamField body="mention" type="string[]">
  List of numbers (or JIDs) to mention. **Group chats only** (`@g.us`). To appear as a clickable link, include `@5511...` in `message` (caption). Without that, they become hidden mentions (notify only).
</ParamField>

<ParamField body="mentionAll" type="boolean" default="false">
  When `true`, mentions **every group member** (except the instance itself). Equivalent to `@everyone`. **Group chats only.**
</ParamField>

<ParamField body="source" type="string" default="api">
  Origin identifier for traceability (e.g., `crm`, `bot-suporte`, `n8n`). Saved on the message record in the database and propagated to webhooks. When omitted, defaults to `"api"`.
</ParamField>

## Notes

<Note>
  * **`delay` is in seconds**, not milliseconds. A value of `3` = 3 seconds of "typing".
  * For `mediaType: "audio"`, **`isVoice` defaults to `true`** automatically when the field is omitted. To send as a music track, you must explicitly send `isVoice: false`.
  * The media can come via **URL** or **base64**. Use `mediaUrl` (which accepts a URL or base64) **or** `mediaBase64`, sending both returns `400 use either mediaUrl or mediaBase64, not both`; sending neither returns `400 mediaUrl or mediaBase64 is required`.
  * When `mediaUrl` is a URL, it must be **publicly accessible**. URLs with authentication, sessions or bot protection commonly fail with `media_download_failed`.
  * For base64 sends, providing `mimeType` is recommended (there is no URL to infer the type). The base64 accepts a data URI (`data:<mime>;base64,...`) or raw base64.
  * When `mimeType` is not provided, the server detects it from the first bytes of the download (`net/http` + sniff). In rare cases (atypical extensions), providing it manually avoids issues.
  * For BR numbers (starting with `55`), the service automatically tries variations with and without the 9th digit.
  * `mention` and `mentionAll` are exclusive to groups. If sent to a DM/channel, the response is `400 Mentions are only supported in group chats`.
  * The `duration` field (audio) is informational, `whatsmeow` still computes its own value from the file. Useful when the server cannot infer it.
  * The `waveform` field is optional and advisory: if omitted, the server generates a standard waveform for PTT.
</Note>

## Errors

| HTTP | Internal status           | Message                                                   |
| ---- | ------------------------- | --------------------------------------------------------- |
| 400  | ,                         | `Instance name is required`                               |
| 400  | ,                         | `Invalid request body: <detail>`                          |
| 400  | ,                         | `Number is required`                                      |
| 400  | ,                         | `MediaType is required`                                   |
| 400  | ,                         | `mediaUrl or mediaBase64 is required`                     |
| 400  | ,                         | `use either mediaUrl or mediaBase64, not both`            |
| 400  | ,                         | `MediaType must be one of: image, video, document, audio` |
| 400  | `invalid_number`          | `Invalid phone number format: <detail>`                   |
| 400  | `mentions_not_supported`  | `Mentions are only supported in group chats`              |
| 400  | `media_download_failed`   | `Failed to download media: <reason>`                      |
| 400  | `media_input_invalid`     | `Failed to resolve media: <reason>`                       |
| 400  | `media_validation_failed` | `Invalid media file: <reason>`                            |
| 400  | `unsupported_media_type`  | `Unsupported media type: <mime>`                          |
| 500  | `media_upload_failed`     | `Failed to upload media to WhatsApp servers`              |
| 500  | `send_failed`             | `Failed to send message: <reason>`                        |
| 404  | ,                         | `Instance not found`                                      |
| 503  | `disconnected`            | `Instance is not connected to WhatsApp`                   |

Error envelope:

```json theme={null}
{
  "success": false,
  "error": { "message": "mediaUrl or mediaBase64 is required" }
}
```
