# Inviare media (immagine · documento · audio · video)

`POST /v1/messages`

**Connect non archivia file.** Per mandare un tuo file, **ospitalo da qualche parte e manda il suo link** (`link`) — lo scarica **Meta**, non noi. Questa è la strada principale, non l'alternativa.

**Gli identificativi (`id`) sono quelli dei file che hai RICEVUTO**: arrivano dentro l'evento di un messaggio in arrivo (`data.message.image.id`, `.document.id`, …). **Non c'è un endpoint di caricamento** — `GET /v1/media/:mediaId` serve solo per scaricare —, quindi **non c'è modo di ottenere un `id` per un tuo file**.

⚠ E la parte scomoda, detta così com'è: Meta **di solito** permette di riusare un identificativo ricevuto per rispondere **dallo stesso numero**, ma **non lo garantisce** e noi **non l'abbiamo ancora verificato con Meta reale**. Non lo affermiamo né lo neghiamo. Se ti importa che parta sempre, usa il link.

**Vuoi mandare SEMPRE lo stesso file** — un listino prezzi, delle istruzioni, un catalogo? **Ospitalo e manda il link.** È la risposta completa: non esiste un `id` tuo da salvare e riusare. E non è una limitazione mascherata — **non conserviamo file perché non conserviamo contenuti** (nemmeno il testo dei tuoi messaggi, salvo la cronologia di Coexistence che scegli di ricevere: cifrata e per 7 giorni al massimo). È esattamente quello che promette la nostra informativa sulla privacy, applicato qui.

**Come si manda.** POST /v1/messages con `type` `image`/`document`/`audio`/`video` e, dentro l'oggetto di quel tipo, **`link` o `id` — esattamente uno dei due**. `caption` per image/document/video; `audio` **non** accetta caption; `document` può avere `filename` (il nome che vede chi lo riceve).

**Cosa serve al link perché Meta lo accetti:** essere **https** e **puntare direttamente al file** (la risposta devono essere i byte, senza login né pagine intermedie); avere il **`Content-Type` corretto** — se non corrisponde al file, Meta lo rifiuta; e rispettare i **limiti di Meta**: immagine JPEG/PNG **5 MB**, audio (aac · amr · mp3 · m4a · ogg _solo OPUS_) **16 MB**, video mp4/3gpp **16 MB**, documento (pdf · doc/docx · xls/xlsx · ppt/pptx · txt) **100 MB**.

⚠ **La trappola più comune: Google Drive, Dropbox e simili NON vanno bene.** Un link di "condivisione" restituisce una **pagina HTML** intermedia, non i byte del file: Meta scarica quella pagina e rifiuta l'invio. Servilo da un URL che risponda direttamente con il file.

## Esempi

### 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"}
```

## Note

- ⚠ Un identificativo che arriva in un webhook **scade in Meta dopo pochi giorni** (la scadenza la fissa Meta). Scaduto, non serve né per scaricarlo né per rimandarlo.
- Meta **tiene in cache il tuo link per 10 minuti**. Se cambi il file dietro lo STESSO URL, aggiungi una query string casuale (`?v=…`) o manderai quello vecchio.
- **Se il link non va bene, lo scopri in due momenti.** (1) All'accettazione, con un 400 e il suo `code`: `MEDIA_LINK_OR_ID_REQUIRED` (non ne hai mandato nessuno), `MEDIA_LINK_AND_ID` (li hai mandati entrambi), `MEDIA_LINK_INVALID` (non è un URL https ben formato), `AUDIO_CAPTION_NOT_ALLOWED`.
- (2) Se ha superato la validazione ma **Meta non è riuscita a scaricarlo**, il rifiuto arriva DOPO il 202: consulta `GET /v1/messages/:id` → `lastErrorCode` = `MEDIA_DOWNLOAD_FAILED` (link inaccessibile o pagina intermedia), `MEDIA_FILE_REJECTED` (formato o dimensione) o `MEDIA_TYPE_UNSUPPORTED`. Cosa fare: servire il file direttamente, con il suo `Content-Type`, entro i limiti indicati sopra.
- Per **scaricare** un file che ti hanno mandato, vedi «Scaricare i media in arrivo».
