Sign inGet Started

Invio di messaggi WhatsApp

Questa guida tratta l'endpoint di invio, POST /v1/whatsapp/messages. Si costruisce un singolo payload JSON con un destinatario e esattamente un tipo di contenuto: un template pre-approvato oppure un messaggio di servizio contenente testo, un'immagine, video, audio, uno sticker, un documento, una posizione, schede contatto o qualcosa da toccare. Bird restituisce 202 Accepted con un ID messaggio e consegna in modo asincrono. Quale dei due puoi inviare dipende dalla finestra di servizio clienti. Ogni richiesta invia un messaggio a un destinatario, e non esiste un endpoint batch.

Un invio minimale

Il payload valido più piccolo è un destinatario to e un template con il suo slug. Aggiungi language se vuoi una lingua specifica; ometterlo invia la lingua predefinita del template. Compila le eventuali variabili dichiarate dal template tramite components.
La chiamata curl indica l'host US; se la tua chiave inizia con bk_eu1_, chiama invece https://eu1.platform.bird.com. Gli SDK leggono la regione dalla chiave, quindi non impostano alcun host.
const msg = await bird.whatsapp.send({
  to: "+15551234567",
  template: {
    slug: "bird_otp",
    components: [{ type: "body", parameters: [{ type: "text", text: "123456" }] }],
  },
});
console.log(msg.id, msg.status);

La finestra di servizio clienti

Quale dei due puoi inviare dipende da un singolo stato: se la finestra di servizio clienti è aperta.
Il contatto apre la finestra inviando un messaggio o chiamando il tuo numero aziendale, e resta aperta per 24 ore, resettandosi ogni volta che ti scrive di nuovo. Finché è aperta puoi inviare un messaggio di servizio, cioè qualsiasi contenuto libero: testo, immagine, video, audio, sticker, documento, posizione o interattivo. Una volta scaduta, solo un template pre-approvato lo raggiunge, e la sua risposta a esso riapre la finestra.
Bird tiene traccia della finestra per te, quindi un messaggio di servizio inviato a una finestra chiusa viene rifiutato prima che qualsiasi cosa venga creata o addebitata: la richiesta restituisce un 422 E15044 WhatsAppServiceWindowClosed. Il controllo usa le informazioni disponibili e consente di proseguire se non può essere completato, quindi un 202 non è la prova che la finestra fosse effettivamente aperta al momento dell'invio; una finestra che scade tra l'accettazione e l'invio fallisce in modo asincrono, con service_window_expired sul last_error del messaggio.
Vedi la finestra di servizio clienti per il ciclo di vita completo: cosa la apre, cosa la resetta e come interagisce con i prezzi.

Costruire il payload

Destinatario

to indica una singola destinazione, specificata come numero di telefono, user ID con ambito business o group ID. Un numero di telefono è in formato E.164: un + iniziale, il prefisso del paese e il numero dell'abbonato, ad esempio +14155550100. Il numero viene validato, quindi un valore che non può essere un numero reale e componibile (lunghezza errata, prefisso non assegnato) viene rifiutato con un 422 WhatsAppInvalidRecipient prima che venga addebitato alcunché. Non esiste un array di destinatari né un invio batch, quindi per raggiungere più persone che non fanno parte di uno stesso gruppo serve una chiamata per destinatario.
Un user ID con ambito business come US.13491208655302741918 indirizza un contatto di cui non hai il numero di telefono, ed è il modo in cui rispondi a un contatto che ti ha raggiunto senza fornirne uno. Cambiano due cose: il numero di invio deve appartenere allo stesso portfolio business a cui è associato l'ID, e un template di codice di verifica monouso richiede un numero di telefono. Uno gestito da Bird viene rifiutato all'accettazione con un 422 WhatsAppRecipientNotSupportedForTemplate; un template di autenticazione creato dal tuo spazio di lavoro viene accettato e poi fallisce, poiché Meta richiede un numero di telefono per esso.
to accetta un'altra forma: un group ID WhatsApp come wag_01krdgeqcxet5s7t44vh8rt9mg, che invia a ogni partecipante di quella chat di gruppo. Un invio di gruppo omette from e riporta la consegna sull'intero gruppo anziché su un singolo destinatario, perciò Invio a un gruppo WhatsApp lo tratta in una pagina dedicata.

Template

template indica il template pre-approvato da inviare:
  • slug (obbligatorio): lo slug del template, ad esempio bird_order_confirmation. Deve corrispondere a un template nel vostro catalogo (lettere minuscole, cifre e underscore).
  • language: il tag lingua del template, ad esempio en o pt-BR. Omettetelo per inviare la lingua predefinita del template; specificare una lingua che il template non possiede restituisce un 422 che elenca quelle disponibili. Il messaggio accettato riporta la lingua risolta.
  • components: i valori che riempiono le variabili del template (vedi Componenti e parametri). Omettetelo per un template senza variabili.
Sfogliate i vostri template, le relative lingue e un'anteprima renderizzata di ciascuno nella pagina Templates.

Componenti e parametri

I template contengono variabili, con nome ({{ref}}, {{amount}}) o numerate ({{1}}, {{2}}). I relativi valori si forniscono tramite components. Ogni componente dichiara un type (body o button) e un array parameters. Ogni parametro dichiara il proprio type (text, image, video, gif, document o location) e porta il campo corrispondente: text una stringa semplice, image/video/gif/document un https url pubblico, e location un punto sulla mappa. Un template con parametri nominali richiede un name su ogni parametro, che corrisponda esattamente ai nomi dichiarati dal template (vedi Riferimento campi). Un template posizionale omette name e accetta i valori nell'ordine di {{n}}, quindi il primo parametro riempie {{1}}. In entrambi i casi, parametri che non corrispondono a quanto dichiarato dal template restituiscono un 422 WhatsAppTemplateParameterMismatch. Esiste anche un tipo di componente header: su un template gestito da Bird viene ignorato, poiché nessun template gestito da Bird dichiara una variabile header, ma su un template creato dal vostro spazio di lavoro viene inoltrato, ed è così che un template utility o marketing con header multimediale ottiene la propria immagine.
Ad esempio, un template per codice monouso il cui body recita {{1}} is your verification code e il cui pulsante copia il codice accetta il codice sia come parametro body sia come parametro button, in modo posizionale (senza name):
Esempio di codice
{
  "components": [
    { "type": "body", "parameters": [{ "type": "text", "text": "481920" }] },
    { "type": "button", "parameters": [{ "type": "text", "text": "481920" }] }
  ]
}

Categoria e mittente

La categoria di un template (authentication, utility o marketing) determina come WhatsApp tratta il messaggio e, insieme al paese di destinazione, quanto costa.
Chi possiede il mittente determina se dovete specificarlo:
  • Un template gestito da Bird (il cui slug inizia con bird_) viene inviato dal numero che Bird mantiene per quella categoria, quindi omettete from. Impostarlo restituisce un 422 WhatsAppSenderNotAllowed.
  • Tutto il resto specifica il proprio mittente in from: un messaggio di servizio di qualsiasi tipo e qualsiasi template creato dal vostro spazio di lavoro. Il numero deve appartenere al vostro spazio di lavoro. Ometterlo restituisce un 422 WhatsAppSenderRequired, e un numero dal quale lo spazio di lavoro non può inviare restituisce un 422 WhatsAppSenderNotFound. Un template creato da voi deve inoltre trovarsi sullo stesso WhatsApp Business Account del numero, altrimenti l'invio restituisce un 422 WhatsAppSenderWABAMismatch.
Configurazione del numero di telefono copre entrambi i tipi di numero e come collegare un numero proprio.

Messaggi di servizio

Al posto di template, inserite esattamente uno tra text, image, video, audio, sticker, document, location, contact_cards o interactive. Tutti e nove sono messaggi di servizio, quindi richiedono una finestra di servizio clienti aperta. Ognuno richiede anche from, un numero di proprietà del vostro spazio di lavoro; i numeri gestiti da Bird non possono trasportarlo.
  • text: { "body": "..." }, fino a 4096 caratteri. Aggiungete "preview_url": true per mostrare un'anteprima del link per il primo URL in body.
  • image, video, audio, sticker, document: ciascuno accetta un URL https pubblico che WhatsApp scarica al momento dell'invio (url), quindi un URL firmato deve restare valido oltre l'invio. Un URL http viene rifiutato immediatamente. WhatsApp scarica il file stesso, quindi un URL non raggiungibile, che serve un tipo non supportato o un file che supera il limite di dimensione per quel tipo viene accettato e poi fallisce, con media_rejected sullo last_error del messaggio e la motivazione propria di WhatsApp in description. image, video e document accettano anche un caption opzionale; document accetta anche un filename opzionale; audio accetta un flag voice opzionale per la resa come nota vocale.
  • location: { "latitude": ..., "longitude": ... } (entrambi obbligatori, gradi decimali) più name e address opzionali.
  • contact_cards: un array di massimo cinque contatti condivisi in un unico messaggio. Il campo name di ogni scheda richiede formatted_name più almeno un'altra parte (first_name, last_name, middle_name, prefix o suffix); phone_numbers, emails, urls e addresses accettano ciascuno fino a dieci voci, mentre org e birthday (come YYYY-MM-DD) sono opzionali. Un phone_number in E.164 aggiunge alla scheda un pulsante che apre una chat con quel contatto.
  • interactive: testo nel body più un elemento interattivo, in uno dei sei tipi: pulsanti di risposta, un menu a lista, un pulsante con link, un carosello multimediale o un singolo pulsante che chiede al destinatario la propria posizione o il proprio numero di telefono. Messaggi interattivi copre la struttura wire di ogni tipo, le risposte generate da un tap e i limiti.
Esempio di codice
{
  "to": "+16505551234",
  "from": "+13124495648",
  "text": { "body": "Your order shipped: https://example.com/track/A1B2C3", "preview_url": true }
}
Una richiesta priva di contenuto, o con più di un tipo, viene rifiutata con un 422.

Citare un messaggio

Impostate in_reply_to_message_id su un message ID WhatsApp per inviare il vostro messaggio come risposta a quello, allo stesso modo in cui toccare Rispondi nell'app WhatsApp cita un messaggio. Il destinatario vede il vostro messaggio con quello citato sopra, e il campo viene restituito a ogni lettura del messaggio.
Funziona anche al contrario: un messaggio in entrata che WhatsApp segna come risposta porta l'ID del messaggio citato nello stesso campo, ed è così che si individua a quale dei vostri messaggi risponde. Un messaggio in entrata che WhatsApp non segna non porta alcun ID, e anche la risoluzione può fallire. Per una correlazione affidabile, usate identificatori di risposta interattiva espliciti insieme allo stato della conversazione o del task memorizzato dalla vostra applicazione. Il campo metadata in uscita resta sul record in uscita e non viene copiato automaticamente sulla risposta.
La citazione viene risolta prima che l'invio sia accettato, quindi una citazione che non può essere renderizzata fa fallire la richiesta stessa e non viene creato né addebitato nulla. Un id che non corrisponde ad alcun messaggio detenuto da questo spazio di lavoro, o più vecchio dei 15 giorni in cui un messaggio resta citabile, restituisce un 404 E15071 WhatsAppReferencedMessageNotFound. Un id che corrisponde a un messaggio mai arrivato a WhatsApp, o a un messaggio di una conversazione diversa rispetto al to e from di questo invio, restituisce un 422 E15072 WhatsAppMessageNotQuotable. Se Bird non riesce a raggiungere lo store che risponde alla domanda, l'invio restituisce un 503 E15073 WhatsAppMessageLookupUnavailable, che vale la pena riprovare. La citazione funziona sia su un invio template sia su un invio a testo libero.
Esempio di codice
{
  "to": "+16505551234",
  "from": "+13124495648",
  "in_reply_to_message_id": "wam_01kya19eknftrs2s6p82asmvnh",
  "text": { "body": "Yes, that slot is still free." }
}

Tag e metadati

Due campi opzionali associano un contesto personalizzato a un messaggio; entrambi vengono restituiti nelle letture API e accompagnano ogni evento webhook relativo al messaggio:
  • tags: fino a 20 etichette { "name": ..., "value": ... } strutturate per dimensioni a bassa cardinalità su cui filtrare e creare report (una campagna, una variante di esperimento). Nomi e valori accettano lettere ASCII, cifre, underscore e trattino; i nomi sono limitati a 32 caratteri e univoci all'interno di un invio, i valori a 64. Filtrate l'elenco messaggi per tag (?tag=campaign o ?tag=campaign:launch-week) e la pagina Metriche suddivide le consegne per tag.
  • metadata: un singolo oggetto JSON arbitrario, fino a 2 KB serializzato, per contesto per-invio che non serve come dimensione di filtro (un ID ordine interno, un riferimento di sessione).
Esempio di codice
{
  "tags": [{ "name": "campaign", "value": "order-confirmations" }],
  "metadata": { "order_id": "ord_8271" }
}

Riferimento campi

CampoTipoObbligatorioLimiti / note
tostringsìUn destinatario per messaggio: un numero di telefono E.164, un user ID con ambito business, che nessun template per codice monouso accetta, o un group ID WhatsApp (wag_…), che invia a ogni partecipante del gruppo
fromstring (E.164)no**Omettere per un template gestito da Bird, che sceglie il proprio mittente, e per un invio di gruppo, che usa il numero del gruppo stesso; obbligatorio per un messaggio di servizio e per un template creato dal vostro spazio di lavoro, e deve essere un numero di proprietà del vostro spazio di lavoro
template.slugstringno**Uno slug di template che il vostro spazio di lavoro può inviare; gli slug gestiti da Bird iniziano con bird_
template.languagestringno*Tag lingua del template (en, pt-BR); omettere per inviare la lingua predefinita del template
template.componentsarraynoRiempie le variabili del template; il type del componente è body o button
template.components[].parameters[].namestringno†Il segnaposto che questo valore riempie, ad esempio ref; obbligatorio e deve corrispondere ai nomi dichiarati dal template per un template con parametri nominali, omesso per uno posizionale
interactiveobjectno**Testo nel body più un tipo di contenuto interattivo; è un messaggio di servizio, quindi richiede una finestra di servizio aperta. Vedi Messaggi interattivi
in_reply_to_message_idstringnoUn message ID WhatsApp detenuto da questo spazio di lavoro, citato nel messaggio inviato; restituito nelle letture. Vedi Citare un messaggio
tagsarraynoFino a 20 etichette {name, value}; nome ≤ 32 caratteri, valore ≤ 64, nomi univoci
metadataobjectnoJSON arbitrario, fino a 2 KB serializzato
* language è opzionale; ometterlo invia la lingua predefinita del template. † name è obbligatorio su ogni parametro per un template con parametri nominali. Ometterlo per uno posizionale. Vedi Componenti e parametri. ** Inserite esattamente uno tra template o un campo contenuto di messaggio di servizio (text, image, video, audio, sticker, document, location, interactive); vedi Messaggi di servizio.

Il modello asincrono: cosa significa 202

Un invio riuscito restituisce 202 Accepted con un ID messaggio e status: accepted. Il 202 viene restituito solo dopo che l'invio è stato accettato in modo durevole; non viene mai accettato e poi scartato silenziosamente. Gli errori definitivi che puoi correggere falliscono immediatamente con un 422: un destinatario non valido, uno slug o una lingua di template sconosciuti, una mancata corrispondenza dei parametri, o un messaggio di servizio inviato in una finestra di servizio clienti chiusa (WhatsAppServiceWindowClosed). Un wallet senza fondi non rientra tra questi: l'invio viene accettato e il messaggio termina in rejected con insufficient_balance quando Bird tenta di addebitarlo. La consegna effettiva avviene in modo asincrono: il messaggio passa a sent quando viene consegnato a WhatsApp, poi a uno stato terminale (delivered o failed) quando arriva la ricevuta, riportato tramite eventi, webhook e gli endpoint di lettura. Una conferma di lettura viene esposta separatamente come timestamp read_at ed evento whatsapp.read, non come stato.
Una nota sulla privacy: per i template di categoria authentication il API non restituisce mai i valori compilati. L'eco del 202 e ogni lettura successiva portano un array components vuoto per quei messaggi, quindi un codice di verifica non riemerge mai.

Riprovare in sicurezza

Inviate l'header Idempotency-Key con un valore univoco per ogni invio logico, e i tentativi successivi diventano sicuri. Se la prima richiesta è andata a buon fine ma non avete mai visto la risposta (timeout, connessione interrotta), reinviarla con la stessa chiave restituisce il risultato originale anziché inviare, e addebitare, un messaggio duplicato. La risposta riprodotta porta un header Idempotency-Replay. Vedi idempotenza per il formato della chiave e la durata di conservazione.

Ricevere la risposta

I messaggi in entrata risiedono sulla stessa risorsa di quelli in uscita, e ognuno di essi resetta la finestra di servizio. Ricevere messaggi WhatsApp copre la loro lettura tramite API, il download dei media inviati da un contatto e il webhook whatsapp.received.

Costi e fatturazione

WhatsApp ha un prezzo per messaggio, basato sulla categoria del template e sul paese del destinatario; vedi Prezzi WhatsApp. Un messaggio viene addebitato in due passaggi, in due momenti diversi, e l'oggetto cost sul messaggio li riporta entrambi:
CampoChe cos'èQuando viene registrato
transaction_amountLa tariffa di Bird per la gestione dell'invioQuando Bird elabora l'invio accettato, prima del dispatch
passthrough_amountLa quota di Meta sul prezzo del messaggio, che Bird inoltraQuando arriva una ricevuta delivered o read applicabile
amountLa somma delle componenti tariffate finoraCresce man mano che ogni componente viene registrata
currency_codeLa valuta del wallet della vostra organizzazione, condivisa da entrambe le componentiCon la prima componente
Entrambi gli importi sono stringhe decimali, al netto delle imposte.
Note: a service message carries no Meta share until September 30th 2026, and neither does a utility template delivered inside an open customer service window. From October 1st 2026 Meta charges for both, except inside a 72-hour free entry point window, and with 1,000 free service messages per business phone number per month: October 2026 pricing changes.
Le due componenti sono tariffate su input diversi. La tariffa di Bird usa la categoria del template inviato e il paese del destinatario, che deriva dal prefisso internazionale del numero di telefono o, per un invio indirizzato a un user ID con ambito business, dal prefisso a due lettere di quell'ID. La quota di Meta usa la categoria che Meta stessa riporta nella ricevuta applicabile, che può differire da quella del template: Meta può riportare authentication-international quando si applicano le sue regole di destinazione, localizzazione dell'azienda e idoneità. Vedi Tariffe authentication-international WhatsApp.
Ciò che cost mostra dipende da quanto il messaggio ha viaggiato:
  • Al momento del 202, cost è null. Nulla è stato tariffato.
  • Dopo l'elaborazione, transaction_amount è impostato e amount lo eguaglia. passthrough_amount resta null.
  • Dopo una ricevuta delivered o read applicabile, un addebito Meta registrato con successo popola passthrough_amount e amount riflette le componenti registrate.
Una componente null significa che nessun importo è registrato in quella proiezione; non è prova che il messaggio fosse gratuito. Una componente esplicitamente tariffata a zero mostra "0.00000".
I due addebiti falliscono in modo diverso. La tariffa di Bird fallisce in modo chiuso: quando non può essere applicata dopo il 202 perché il wallet non può coprire l'invio o il percorso non ha un prezzo configurato, il messaggio termina in rejected con il codice di errore insufficient_balance o price_not_found, e nulla viene addebitato. Un messaggio rejected non ha mai raggiunto WhatsApp, ed è ciò che lo distingue da failed. La quota di Meta fallisce in modo aperto: se il wallet è insufficiente o la tariffa è mancante quando arriva la ricevuta, l'addebito viene saltato senza annullare lo stato osservato del messaggio. La vostra consegna non viene mai bloccata dal secondo addebito.
Un messaggio addebitato da Bird mantiene quell'addebito in uscita anche se la consegna successivamente fallisce. La tariffa Meta viene elaborata da un callback delivered o read applicabile quando Meta riporta un pricing regolare con categoria e destinazione risolvibili. Entrambi i percorsi di callback usano la stessa identità di tariffa e si affidano alla deduplicazione del servizio di fatturazione. Riconciliate le ricevute riprodotte con i record di fatturazione anziché trattare la proiezione del messaggio come una ricevuta di addebito permanente. Il pricing di servizio o free-entry può rendere la componente Meta pari a zero; una componente non risolta non è prova che il messaggio fosse gratuito.
Usate il registro di fatturazione per la riconciliazione finanziaria. I campi cost del messaggio sono proiezioni degli addebiti e possono essere in ritardo o restare incompleti. Vedi Metriche WhatsApp per la distinzione tra osservazioni sui messaggi e record di fatturazione.
Gli eventi WhatsApp non includono i costi. Per leggere una delle due componenti, rileggete il messaggio con GET /v1/whatsapp/messages/{id}.

Prossimi passi