# Enviar media (imagen · documento · audio · video)

`POST /v1/messages`

**Connect no almacena archivos.** Para mandar un archivo tuyo, **hospedalo y mandá su enlace** (`link`) — lo descarga **Meta**, no nosotros. Ese es el camino principal, no la alternativa.

**Los identificadores (`id`) son los de archivos que RECIBISTE**: llegan dentro del evento de un mensaje entrante (`data.message.image.id`, `.document.id`, …). **No hay endpoint de subida** — `GET /v1/media/:mediaId` es sólo de descarga —, así que **no existe forma de obtener un `id` para un archivo tuyo**.

⚠ Y la parte incómoda, dicha como es: Meta **suele** permitir reusar un identificador entrante para responder **desde el mismo número**, pero **no lo garantiza** y nosotros **todavía no lo verificamos contra Meta real**. No lo afirmamos ni lo negamos. Si te importa que salga siempre, usá el enlace.

**¿Querés mandar SIEMPRE el mismo archivo** — una lista de precios, un instructivo, un catálogo? **Hospedalo y mandá el enlace.** Es la respuesta completa: no hay un `id` propio que guardar y reusar. Y no es una limitación disimulada — **no guardamos archivos porque no guardamos contenido** (tampoco el texto de tus mensajes). Es exactamente lo que promete nuestra política de privacidad, aplicado acá.

**Cómo se manda.** POST /v1/messages con `type` `image`/`document`/`audio`/`video` y, dentro del objeto de ese tipo, **`link` o `id` — exactamente uno de los dos**. `caption` para image/document/video; `audio` **no** acepta caption; `document` puede llevar `filename` (el nombre que ve quien lo recibe).

**Qué necesita el enlace para que Meta lo acepte:** ser **https** y **apuntar directo al archivo** (que la respuesta sean los bytes, sin login ni página intermedia); traer el **`Content-Type` correcto** — si no coincide con el archivo, Meta lo rechaza; y respetar los **límites de Meta**: imagen JPEG/PNG **5 MB**, audio (aac · amr · mp3 · m4a · ogg _sólo OPUS_) **16 MB**, video mp4/3gpp **16 MB**, documento (pdf · doc/docx · xls/xlsx · ppt/pptx · txt) **100 MB**.

⚠ **La trampa más común: Google Drive, Dropbox y similares NO sirven.** Un enlace de "compartir" devuelve una **página HTML** intermedia, no los bytes del archivo: Meta descarga esa página y rechaza el envío. Servilo desde una URL que responda el archivo directo.

## Ejemplos

### cURL

```bash
curl -X POST https://api.waiaconnect.com/v1/messages \
  -H "Authorization: Bearer wc_live_YOUR_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"connectionId":"conn_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX","to":"5493511234567","type":"image","image":{"link":"https://example.com/product.jpg","caption":"New product 📦"}}'
# document: {"type":"document","document":{"link":"https://…/invoice.pdf","filename":"invoice.pdf","caption":"Your invoice"}}
# audio (no caption): {"type":"audio","audio":{"link":"https://…/note.ogg"}}
# video: {"type":"video","video":{"link":"https://…/demo.mp4","caption":"Demo"}}
# by media id instead of a link: {"image":{"id":"1234567890"}}
# ...but the only ids that exist are the ones you RECEIVED (from a message.received
# event); there is no upload endpoint. To send a file of your own, host it and link it.
```

### Node.js

```javascript
// Media is sent by { link } (Meta fetches the URL) or { id }. The only media ids that
// exist are the ones you RECEIVED: there is no upload endpoint. Host your own files.
const res = await fetch("https://api.waiaconnect.com/v1/messages", {
  method: "POST",
  headers: {
    "Authorization": "Bearer wc_live_YOUR_API_KEY",
    "Idempotency-Key": crypto.randomUUID(),
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
  "connectionId": "conn_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
  "to": "5493511234567",
  "type": "image",
  "image": {
    "link": "https://example.com/product.jpg",
    "caption": "New product 📦"
  }
})
});
console.log(await res.json()); // { id: "msg_…", status: "queued" }
```

### PHP

```php
<?php
// Media is sent by ["link" => …] (Meta fetches it) or ["id" => …]. The only media ids
// that exist are the ones you RECEIVED: there is no upload endpoint. Host your own files.
$ch = curl_init("https://api.waiaconnect.com/v1/messages");
curl_setopt_array($ch, [
  CURLOPT_POST => true,
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => [
    "Authorization: Bearer wc_live_YOUR_API_KEY",
    "Idempotency-Key: " . bin2hex(random_bytes(16)),
    "Content-Type: application/json",
  ],
  CURLOPT_POSTFIELDS => json_encode(["connectionId" => "conn_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX","to" => "5493511234567","type" => "image","image" => ["link" => "https://example.com/product.jpg","caption" => "New product 📦"]]),
]);
echo curl_exec($ch);
```

### Python

```python
import requests, uuid
# Media is sent by {"link": …} (Meta fetches it) or {"id": …}. The only media ids that
# exist are the ones you RECEIVED: there is no upload endpoint. Host your own files.
res = requests.post("https://api.waiaconnect.com/v1/messages",
  headers={
    "Authorization": "Bearer wc_live_YOUR_API_KEY",
    "Idempotency-Key": str(uuid.uuid4()),
  },
  json={"connectionId":"conn_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX","to":"5493511234567","type":"image","image":{"link":"https://example.com/product.jpg","caption":"New product 📦"}})
print(res.json())  # {"id": "msg_…", "status": "queued"}
```

## Notas

- ⚠ Un identificador que llega en un webhook **vence a los 7 días** (los que devuelve la API de subida de Meta duran 30). Vencido no sirve ni para descargarlo ni para reenviarlo.
- Meta **cachea tu enlace 10 minutos**. Si cambiás el archivo detrás de la MISMA URL, agregale un query string al azar (`?v=…`) o vas a mandar el viejo.
- **Si el enlace no sirve, te enterás en dos momentos.** (1) Al aceptar, con un 400 y su `code`: `MEDIA_LINK_OR_ID_REQUIRED` (no mandaste ninguno), `MEDIA_LINK_AND_ID` (mandaste los dos), `MEDIA_LINK_INVALID` (no es una URL https bien formada), `AUDIO_CAPTION_NOT_ALLOWED`.
- (2) Si pasó la validación pero **Meta no pudo bajarlo**, el rechazo aterriza DESPUÉS del 202: consultá `GET /v1/messages/:id` → `lastErrorCode` = `MEDIA_DOWNLOAD_FAILED` (enlace inaccesible o página intermedia), `MEDIA_FILE_REJECTED` (formato o tamaño) o `MEDIA_TYPE_UNSUPPORTED`. Qué hacer: servir el archivo directo, con su `Content-Type`, dentro de los límites de arriba.
- Para **descargar** un archivo que te mandaron a vos, ver «Descargar media entrante».
