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

# Enviar media

> Envía una imagen, video, audio (voz/regular) o documento por URL o base64

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

## Descripción

Envía un archivo de media (`image`, `video`, `document` o `audio`) desde una **URL pública** o un **string base64**. La fuente se puede indicar en `mediaUrl` (que acepta tanto la URL como el base64 de la media) **o** en el campo `mediaBase64`, envía uno u otro, nunca ambos. Soporta `message` como subtítulo, `replyTo` (cita por ID), `replyPrivate`, `mention` / `mentionAll` (solo chats de grupo), `delay` (en segundos) para simular tipeo real y, para audio, `isVoice` (PTT), `duration` y `waveform`. El servidor descarga el archivo (cuando es URL) o decodifica el base64, detecta el `mimeType` cuando se omite y lo sube a los servidores de WhatsApp antes de enviar.

## Ejemplos

### Imagen por URL

Envía una imagen (`mediaType: "image"`) descargada desde una URL pública, con `message` usado como el subtítulo que aparece bajo la foto en el 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>

### Imagen por base64

En lugar de una URL, envía el contenido de la media en base64. Puedes poner el base64 en el propio `mediaUrl` (con o sin el prefijo data URI `data:image/jpeg;base64,`) **o** en el campo dedicado `mediaBase64`. Como no hay URL para inferir el nombre/tipo, se recomienda indicar `mimeType` (y `fileName`, para documentos).

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

### Audio de voz (PTT)

Cuando `mediaType: "audio"` e `isVoice` se omite, el servidor asume `true` por defecto (mensaje de voz/PTT). Para enviar como audio "regular" (una pista musical, por ejemplo), pasa `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>

### Documento (PDF) con `fileName`

Envía un PDF como documento. `fileName` (`Contract-2026.pdf`) define el nombre mostrado en la tarjeta de adjunto y `message` aparece como texto acompañante. Sin `fileName`, WhatsApp muestra un nombre genérico.

<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 con subtítulo y delay

Envía un video MP4 con el subtítulo "Check out this video!" y `delay: 3`, el servidor envía el indicador "escribiendo..." durante 3 segundos antes de disparar el video, simulando tipeo real.

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

### Imagen en grupo con respuesta y mención

Envía una imagen en un grupo (`@g.us`), citando un mensaje anterior vía `replyTo` y mencionando a un miembro vía el array `mention`. `@5511888888888` en el subtítulo se vuelve clicable y dispara una notificación para el usuario etiquetado.

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

## Respuesta exitosa

El `messageType` refleja el `mediaType` que enviaste (`image`, `video`, `document` o `audio`). Los metadatos resueltos por el upload aparecen en `mediaUrl` (URL re-emitida para `mmg.whatsapp.net`), `mediaMimeType` y `mediaSize`. Para audio PTT, el servidor también retorna `mediaDuration` cuando puede calcularla.

```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>
  Para `mediaType: "document"`, `fileName` aparece en la tarjeta. Para `mediaType: "audio"` con `isVoice: true`, el mensaje se entrega como PTT (waveform + ícono de micrófono). Para audio "regular" (una pista musical), usa `isVoice: false`.
</Note>

## Parámetros de ruta

<ParamField path="instance" type="string" required>
  Nombre de la instancia (p. ej., `$Instance_Name`).
</ParamField>

## Cabeceras

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

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

## Cuerpo de la solicitud

<ParamField body="number" type="string" required>
  Destino: teléfono (`5511999999999`) o JID (`@s.whatsapp.net`, `@lid`, `@g.us`, `@newsletter`).
</ParamField>

<ParamField body="mediaType" type="string" required>
  Uno de: `image`, `video`, `document`, `audio`. Determina cómo WhatsApp renderiza el mensaje.
</ParamField>

<ParamField body="mediaUrl" type="string">
  Fuente de la media: una **URL pública** del archivo **o** el **base64** de la media (con data URI `data:<mime>;base64,...` o base64 crudo). Cuando es una URL, debe ser accesible públicamente (sin autenticación) y el servidor la descarga. Debes indicar `mediaUrl` **o** `mediaBase64` (nunca ambos).
</ParamField>

<ParamField body="mediaBase64" type="string">
  Base64 de la media (alternativa a `mediaUrl`), con data URI `data:<mime>;base64,...` o base64 crudo. **No se puede usar junto con `mediaUrl`**, envía uno u otro. Como no hay URL, se recomienda indicar `mimeType` (y `fileName` para documentos).
</ParamField>

<ParamField body="message" type="string">
  Subtítulo de la media. Para `mediaType: "document"`, aparece como texto acompañante. Opcional para todos los tipos.
</ParamField>

<ParamField body="mimeType" type="string">
  Tipo MIME del archivo (p. ej., `image/jpeg`, `application/pdf`). Cuando se omite, el servidor lo detecta automáticamente desde la descarga.
</ParamField>

<ParamField body="fileName" type="string">
  Nombre del archivo mostrado en la tarjeta. **Recomendado para `mediaType: "document"`**, sin él, WhatsApp muestra un nombre genérico.
</ParamField>

<ParamField body="isVoice" type="boolean" default="true (for audio)">
  Aplica solo a `mediaType: "audio"`. Cuando es `true`, el mensaje se entrega como **PTT** (mensaje de voz, con waveform). Cuando es `false`, se entrega como audio regular (una pista musical). Cuando el campo se omite en `audio`, el servidor asume `true`.
</ParamField>

<ParamField body="duration" type="uint32">
  Duración del audio en segundos. Aplica solo a `mediaType: "audio"`. Opcional, cuando se omite, el servidor intenta detectarla automáticamente.
</ParamField>

<ParamField body="waveform" type="byte[]">
  Waveform de audio precomputada (PTT). Opcional, cuando se omite, el servidor genera automáticamente una waveform default. Aplica solo a `mediaType: "audio"` con `isVoice: true`.
</ParamField>

<ParamField body="delay" type="int" default="0">
  Tiempo en **segundos** a esperar antes de enviar. Durante el intervalo, el servidor envía el indicador "escribiendo..." al destinatario y dispara "pausado" antes del envío real.
</ParamField>

<ParamField body="replyTo" type="string">
  ID del mensaje a citar (respuesta). El mensaje original debe pertenecer a la misma instancia y haber sido guardado en la base de datos. Posibles errores: `reply_message_not_found`, `reply_message_instance_mismatch`.
</ParamField>

<ParamField body="replyPrivate" type="boolean" default="false">
  Cuando es `true` **y** `replyTo` apunta a un mensaje originado en un grupo, la respuesta se redirige al chat **privado** del autor original (manteniendo la cita). Ignorado si el mensaje original no es de un grupo.
</ParamField>

<ParamField body="mention" type="string[]">
  Lista de números (o JIDs) a mencionar. **Solo en chats de grupo** (`@g.us`). Para aparecer como enlace clicable, incluye `@5511...` en `message` (subtítulo). Sin eso, se vuelven menciones ocultas (solo notifican).
</ParamField>

<ParamField body="mentionAll" type="boolean" default="false">
  Cuando es `true`, menciona a **todos los miembros del grupo** (excepto la propia instancia). Equivalente a `@everyone`. **Solo en chats de grupo.**
</ParamField>

<ParamField body="source" type="string" default="api">
  Identificador de origen para trazabilidad (p. ej., `crm`, `bot-suporte`, `n8n`). Guardado en el registro del mensaje en la base de datos y propagado a los webhooks. Cuando se omite, el default es `"api"`.
</ParamField>

## Notas

<Note>
  * **`delay` es en segundos**, no milisegundos. Un valor de `3` = 3 segundos de "escribiendo".
  * Para `mediaType: "audio"`, **`isVoice` asume `true`** automáticamente cuando el campo se omite. Para enviar como pista musical, debes enviar explícitamente `isVoice: false`.
  * La media puede venir por **URL** o **base64**. Usa `mediaUrl` (que acepta URL o base64) **o** `mediaBase64`, enviar ambos retorna `400 use either mediaUrl or mediaBase64, not both`; no enviar ninguno retorna `400 mediaUrl or mediaBase64 is required`.
  * Cuando `mediaUrl` es una URL, debe ser **accesible públicamente**. URLs con autenticación, sesiones o protección anti-bot comúnmente fallan con `media_download_failed`.
  * Para envíos en base64, se recomienda indicar `mimeType` (no hay URL para inferir el tipo). El base64 acepta data URI (`data:<mime>;base64,...`) o base64 crudo.
  * Cuando `mimeType` no se proporciona, el servidor lo detecta desde los primeros bytes de la descarga (`net/http` + sniff). En casos raros (extensiones atípicas), proporcionarlo manualmente evita problemas.
  * Para números BR (que comienzan con `55`), el servicio prueba automáticamente variaciones con y sin el 9° dígito.
  * `mention` y `mentionAll` son exclusivos de grupos. Si se envían a un DM/canal, la respuesta es `400 Mentions are only supported in group chats`.
  * El campo `duration` (audio) es informativo, `whatsmeow` aún calcula su propio valor desde el archivo. Útil cuando el servidor no puede inferirlo.
  * El campo `waveform` es opcional y consultivo: si se omite, el servidor genera una waveform estándar para PTT.
</Note>

## Errores

| HTTP | Status interno            | Mensaje                                                   |
| ---- | ------------------------- | --------------------------------------------------------- |
| 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`                   |

Envoltorio de error:

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