GET /v1/messages/:id con il msg_… restituito dal POST (un id numerico → 404). Un unico vocabolario di stato: queued | sending | sent | delivered | read | failed | held. ⚠ Finali: read, failed e held (un messaggio trattenuto non lo rimandiamo mai: se serve, rimandalo tu a partire da heldUntil). delivered NON è finale (delivered→read è valido) — se interrompi il polling su delivered non vedi la lettura.
Un rifiuto di WhatsApp NON torna come errore HTTP. Il POST ha già risposto 202 (accettato, non inviato); se Meta lo rifiuta, lo scopri qui: il messaggio resta failed (o held) con lastErrorCode, un codice stabile su cui puoi fare switch — WINDOW_24H_EXPIRED, RECIPIENT_OPTED_OUT, CONNECTION_TOKEN_REVOKED… Il lastError che arriva accanto è un aiuto in inglese per un umano, non il contratto. Il catalogo completo è nel modulo degli errori.
E non serve chiedere: te lo segnala il webhook. Se Meta rifiuta il nostro invio (modello inesistente, parametro non valido, numero disattivato…), ti arriva un message.status con status: "failed", stage: "send", lo stesso failureCode che vedi qui e messageId = il tuo msg_…. Una sola volta per messaggio. Le conferme di CONSEGNA di ciò che hai mandato tramite l'API portano connectMessageId = il tuo msg_… (lì il messageId è il wamid di Meta); se Meta lo rifiuta in consegna, stage: "delivery" + failureCode, e lo stesso codice resta in lastErrorCode.
curl https://api.waiaconnect.com/v1/messages/msg_5d325780fe504d0f87eb45009d28ae4f \
-H "Authorization: Bearer wc_live_YOUR_API_KEY"
# → { "id": "msg_…", "status": "delivered", "attempts": 1, "createdAt": "…Z", … }Non conserviamo contenuti: questa risposta contiene metadati (stato, tentativi, holdReason), mai il testo del messaggio.
Il rifiuto più comune è WINDOW_24H_EXPIRED: hai mandato testo libero a qualcuno che non ti ha scritto nelle ultime 24 ore. Si risolve con un modello approvato.