Inviare un messaggio WhatsApp via API: cURL, Node, Python, PHP
Come inviare un messaggio WhatsApp con una API: esempi in cURL, Node.js, Python e PHP per template e messaggi di sessione, con stati ed errori.
Di OmniChannels.ai Editorial Team · Revisione di BroadNet Messaging Solutions Team
Integrazione
Risposta breve
Con l'API di OmniChannels.ai inviate ogni messaggio WhatsApp tramite un unico endpoint, POST https://api.broadnet.me/whatsapp/message/sendbulk, come multipart form data con un header Authorization: Bearer. Il campo messageType seleziona il payload: TEMPLATE per i template approvati, necessari per avviare una conversazione o scrivere fuori dalla finestra di 24 ore, e TEXT, IMAGE o INTERACTIVE per le risposte al suo interno.
In questa pagina
- Come si invia un messaggio WhatsApp tramite l'API?
- Template o messaggio di sessione: quale inviare?
- Come si invia un template approvato?
- Quali campi della richiesta contano di più?
- Come si invia un template con un pulsante o un OTP?
- Come si risponde all'interno della finestra di assistenza clienti?
- Come si traccia la consegna?
- Quali errori devo gestire?
- Esempio pratico: un servizio di notifica degli ordini
- Quali sono gli errori comuni?
- Come vi aiuta OmniChannels.ai
Punti chiave
- Tutti i messaggi WhatsApp passano da POST /whatsapp/message/sendbulk; messageType (TEMPLATE, TEXT, IMAGE, INTERACTIVE e altri) decide quali campi sono obbligatori.
- La richiesta è multipart/form-data, non JSON, quindi in Python serve files= e in PHP un array come corpo per evitare l'url-encoding.
- Fuori dalla finestra di assistenza clienti di 24 ore si può inviare solo un template approvato; al suo interno si possono inviare testo libero, contenuti multimediali e messaggi interattivi.
- La chiamata mette in coda i messaggi e restituisce subito un batchId; gli stati per destinatario arrivano al vostro webhook e dall'endpoint bulk-insights.
- L'API non accetta una chiave di idempotenza, quindi registrate ogni invio prima di effettuarlo e dopo un timeout controllate lo stato invece di reinviare.
Questa guida è per gli sviluppatori che integrano l'invio WhatsApp in un'applicazione. Mostra richieste funzionanti in cURL, Node.js, Python e PHP, spiega la differenza tra messaggi template e di sessione e tratta tracciamento ed errori. Ogni endpoint e campo deriva dal nostro riferimento dell'API WhatsApp e dalla specifica OpenAPI.
Come si invia un messaggio WhatsApp tramite l'API?
Eseguite una POST autenticata a https://api.broadnet.me/whatsapp/message/sendbulk con campi di form. Il campo messageType sceglie il tipo di messaggio e la risposta conferma che i messaggi sono stati messi in coda.
| Elemento | Valore |
|---|---|
| URL di base | https://api.broadnet.me (senza prefisso /v1) |
| Endpoint | POST /whatsapp/message/sendbulk |
| Autenticazione | Authorization: Bearer $BROADNET_TOKEN |
| Content type | multipart/form-data (impostato automaticamente dal client HTTP) |
| Risposta | Riepilogo JSON del batch con un batchId |
Le credenziali di test vengono rilasciate dopo una breve richiesta di accesso e gli invii di prova vanno ai vostri numeri verificati. Vedi il quickstart e le pagine sull'autenticazione. Tenete il token solo sul vostro server.
Template o messaggio di sessione: quale inviare?
Inviate un template per avviare una conversazione o per scrivere fuori dalla finestra, e un messaggio di sessione quando il cliente vi ha scritto nelle ultime 24 ore. Questa regola la stabilisce Meta, non noi.
| Messaggio template | Messaggio di sessione | |
|---|---|---|
messageType |
TEMPLATE |
TEXT, IMAGE, VIDEO, AUDIO, DOCUMENT, INTERACTIVE e altri |
| Quando è consentito | In qualsiasi momento, una volta approvato il template | Solo mentre la finestra di assistenza clienti è aperta |
| Richiede approvazione | Sì, da parte di Meta | No |
| Uso tipico | Aggiornamenti sugli ordini, OTP, promemoria, campagne | Risposte di assistenza, risposte a domande |
Meta addebita ogni messaggio consegnato in base alla categoria e le sue regole per i messaggi di servizio e i template di utilità sono cambiate il 1° ottobre 2026. La nostra guida alla finestra tratta la meccanica, mentre la pagina dei prezzi e il calcolatore trattano i costi.
Come si invia un template approvato?
Impostate messageType=TEMPLATE, indicate il nome del template e il suo codice lingua e passate i valori delle variabili. Create batchId=0 per avviare un nuovo batch.
curl -X POST https://api.broadnet.me/whatsapp/message/sendbulk \
-H "Authorization: Bearer $BROADNET_TOKEN" \
-F batchId=0 \
-F campaignName=order-updates \
-F destinationNo=971500000000 \
-F messageType=TEMPLATE \
-F templateName=order_update \
-F templateLanguage=en_US \
-F bodyTemplateVariables=INV-400
// Node.js 18+ (fetch e FormData integrati)
const form = new FormData();
form.append("batchId", "0");
form.append("campaignName", "order-updates");
form.append("destinationNo", "971500000000");
form.append("messageType", "TEMPLATE");
form.append("templateName", "order_update");
form.append("templateLanguage", "en_US");
form.append("bodyTemplateVariables", "INV-400");
const res = await fetch("https://api.broadnet.me/whatsapp/message/sendbulk", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.BROADNET_TOKEN}` }, // fetch imposta il boundary multipart
body: form,
});
console.log(await res.json());
import os
import requests
fields = {
"batchId": "0",
"campaignName": "order-updates",
"destinationNo": "971500000000",
"messageType": "TEMPLATE",
"templateName": "order_update",
"templateLanguage": "en_US",
"bodyTemplateVariables": "INV-400",
}
# Inviate ogni campo come parte multipart (files=) e non url-encoded (data=).
r = requests.post(
"https://api.broadnet.me/whatsapp/message/sendbulk",
headers={"Authorization": f"Bearer {os.environ['BROADNET_TOKEN']}"},
files={k: (None, v) for k, v in fields.items()},
timeout=30,
)
print(r.json())
<?php
$ch = curl_init("https://api.broadnet.me/whatsapp/message/sendbulk");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ["Authorization: Bearer " . getenv("BROADNET_TOKEN")],
// Un array (non una stringa) fa inviare a cURL multipart/form-data.
CURLOPT_POSTFIELDS => [
"batchId" => "0",
"campaignName" => "order-updates",
"destinationNo" => "971500000000",
"messageType" => "TEMPLATE",
"templateName" => "order_update",
"templateLanguage" => "en_US",
"bodyTemplateVariables" => "INV-400",
],
]);
echo curl_exec($ch);
curl_close($ch);
La destinazione è prefisso internazionale più numero, solo cifre. La chiamata risponde subito con un riepilogo del batch:
{
"totalFiles": 1,
"totalSuccess": 1,
"totalFailed": 0,
"totalDuplicates": 0,
"batchId": "2609091405000000001",
"fileResults": [
{
"fileName": "direct",
"totalRecords": 1,
"successCount": 1,
"failedRecipients": [],
"duplicateRecipients": []
}
],
"message": "Messages queued for delivery"
}
Memorizzate il batchId accanto al vostro record. «In coda» non significa «consegnato».
Quali campi della richiesta contano di più?
I campi obbligatori dipendono da messageType. Questi sono i più comuni.
| Campo | Obbligatorio | Descrizione |
|---|---|---|
messageType |
Sì | TEXT, IMAGE, VIDEO, AUDIO, DOCUMENT, STICKER, LOCATION, CONTACTS, TEMPLATE, INTERACTIVE, REACTION o FLOW |
destinationNo o destinationFiles |
Uno dei due | Numeri separati da virgole, punti e virgola o a capo; oppure un file TXT, CSV o XLSX |
batchId |
No | 0 avvia un nuovo batch |
campaignName |
No | La vostra etichetta per la reportistica |
allowDuplicate |
No | false rimuove i destinatari duplicati |
templateName, templateLanguage |
Per TEMPLATE |
Nome del template approvato e codice lingua, come en_US o ar |
bodyTemplateVariables |
Per template | Valore della variabile del corpo; i parametri con nome si passano come JSON |
buttonsTemplateVariable |
Per template | Valore dinamico del pulsante: suffisso URL, codice coupon o codice OTP |
content |
Per TEXT |
Corpo del testo, fino a 4.096 caratteri, o didascalia per i contenuti multimediali |
mediaUrl |
Per tipo | URL HTTPS pubblico del contenuto multimediale |
interactivePayload |
Per INTERACTIVE |
Oggetto interattivo di Meta come stringa JSON |
La programmazione dell'invio usa scheduled, scheduleTime e gmt, mentre delay rallenta la consegna. L'elenco completo è nel riferimento dell'API WhatsApp.
Come si invia un template con un pulsante o un OTP?
Aggiungete il valore del pulsante in buttonsTemplateVariable. Per un template di autenticazione con pulsante «copia codice», passate il codice sia nel corpo sia nei campi del pulsante.
curl -X POST https://api.broadnet.me/whatsapp/message/sendbulk \
-H "Authorization: Bearer $BROADNET_TOKEN" \
-F batchId=0 \
-F destinationNo=971500000000 \
-F messageType=TEMPLATE \
-F templateName=login_code \
-F templateLanguage=en_US \
-F bodyTemplateVariables=482913 \
-F buttonsTemplateVariable=482913
Qui login_code è un segnaposto per il vostro template di autenticazione approvato. Con più pulsanti, buttonsTemplateVariable accetta un array JSON. Generate il codice sul server, memorizzatelo come hash con una scadenza e non registratelo mai nei log. La nostra guida agli OTP tratta la consegna one-tap, zero-tap e copy-code.
Come si risponde all'interno della finestra di assistenza clienti?
Usate un tipo di messaggio di sessione. Una risposta in testo semplice richiede messageType=TEXT e content.
curl -X POST https://api.broadnet.me/whatsapp/message/sendbulk \
-H "Authorization: Bearer $BROADNET_TOKEN" \
-F batchId=0 \
-F destinationNo=971500000000 \
-F messageType=TEXT \
-F campaignName=support \
-F "content=Your order has shipped. Reply here if you need anything."
Per i pulsanti di risposta, inviate messageType=INTERACTIVE e un interactivePayload che segua il formato dei messaggi interattivi di Meta.
curl -X POST https://api.broadnet.me/whatsapp/message/sendbulk \
-H "Authorization: Bearer $BROADNET_TOKEN" \
-F batchId=0 \
-F destinationNo=971500000000 \
-F messageType=INTERACTIVE \
-F campaignName=support \
-F 'interactivePayload={"type":"button","body":{"text":"Confirm your delivery slot?"},"action":{"buttons":[{"type":"reply","reply":{"id":"slot_ok","title":"Confirm"}},{"type":"reply","reply":{"id":"slot_change","title":"Change"}}]}}'
Per segnare come letto un messaggio in arrivo, e facoltativamente mostrare l'indicatore di scrittura, chiamate POST /whatsapp/message/read con JSON:
curl -X POST https://api.broadnet.me/whatsapp/message/read \
-H "Authorization: Bearer $BROADNET_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "messageId": "wamid.HBgMOTcxNTAwMDAwMDAwFQIAEhgg", "typing": true }'
Consultate la nostra guida ai messaggi interattivi per indicazioni di progettazione.
Come si traccia la consegna?
Gli stati vengono inviati al webhook configurato sul vostro account e si possono leggere anche per batch. Ogni destinatario passa da SUBMITTED, SENT, DELIVERED e READ, oppure termina come FAILED o REJECTED con un motivo.
curl "https://api.broadnet.me/whatsapp/message/bulk-insights?batch_Id=2609091405000000001&status=FAILED&page=0&size=50" \
-H "Authorization: Bearer $BROADNET_TOKEN"
batch_Id è obbligatorio; status, destination, search, page e size sono facoltativi e page parte da zero. Usate il polling come rete di sicurezza per gli eventi persi e costruite il vostro ricevitore come descritto nella nostra guida ai webhook. I batch si possono anche mettere in pausa, riprendere e interrompere con gli endpoint dei batch descritti nel riferimento.
Quali errori devo gestire?
Leggete prima lo stato HTTP, poi il corpo dell'errore. L'API segnala gli errori in un corpo JSON a cinque campi e i fallimenti specifici di WhatsApp possono provenire da Meta.
| Stato | Significato | Azione |
|---|---|---|
400 |
Errore di validazione, ad esempio un campo specifico del tipo mancante o una destinazione non valida | Correggete la richiesta; non riprovate invariata |
401 / 403 |
Token mancante o non valido, oppure operazione non consentita | Correggete il token o chiedeteci di abilitare l'operazione |
404 |
Template o mittente non trovato | Controllate nome e lingua del template |
413 |
File caricato troppo grande | Riducete il file |
429 |
Limite di richieste | Attendete Retry-After |
502 |
Meta ha rifiutato la chiamata; message riporta codice e testo dell'errore di Meta |
Leggetelo; riprovate solo se transitorio |
500, 503, 504 |
Problemi del server o a monte | Riprovate con backoff esponenziale e jitter |
La nostra guida ai codici di errore spiega i codici Meta più comuni. Poiché non esiste una chiave di idempotenza, seguite i passaggi di protezione dai duplicati in Errori e limiti: scrivete prima un record in sospeso, memorizzate il batchId restituito e dopo un timeout controllate lo stato prima di inviare di nuovo.
Esempio pratico: un servizio di notifica degli ordini
«Souq Rafiq» (nome di fantasia), un rivenditore degli Emirati, invia un template order_update quando un ordine viene spedito.
- Il servizio ordini scrive una riga con chiave
order-8842-shippede statopending. - Chiama
sendbulkcon il template e il numero d'ordine comebodyTemplateVariables. - Con
200memorizza ilbatchIde segna la riga comeaccepted. - Il gestore del webhook aggiorna la riga a
DELIVEREDoREAD, oppureFAILEDcon un motivo. - Se il cliente risponde, lo strumento di assistenza usa un messaggio di sessione
TEXTall'interno della finestra. - Un job notturno legge
bulk-insightsper i batch del giorno prima e riconcilia gli eventuali stati mancanti.
Quali sono gli errori comuni?
- Inviare JSON. L'endpoint di invio WhatsApp è multipart;
Content-Type: application/jsonnon funziona. data=in Python invece difiles=, che esegue l'url-encoding del form.- Testo libero fuori dalla finestra, che fallisce. Usate un template.
- Codice lingua sbagliato, come
eninvece dien_USdel template. - Considerare
200come consegnato. Significa in coda. - Nuovi tentativi alla cieca dopo un timeout, che possono scrivere due volte a un cliente.
- Token nel codice client. Chiamate l'API solo dal backend.
- Limiti: i dettagli del payload variano per template. Confermate con il nostro team la struttura delle variabili per la prima campagna personalizzata.
Come vi aiuta OmniChannels.ai
BroadNet è un Meta Business Solution Provider ufficiale e l'attivazione sulla WhatsApp Business API usa l'Embedded Signup di Meta; il nostro team prepara la registrazione con voi, mentre le approvazioni restano a Meta. L'API REST è documentata con una specifica OpenAPI 3.1 e un SDK TypeScript, @broadnet/cpaas-sdk, e gli stati di consegna sono normalizzati tra i canali. Partite dalla documentazione dell'API WhatsApp o dal quickstart.
Domande frequenti
I messaggi WhatsApp si inviano come JSON o come form data?
Come form data. L'endpoint di invio WhatsApp, POST /whatsapp/message/sendbulk, accetta multipart/form-data, con ogni campo come parte del form. Gli invii singoli di SMS usano invece JSON, il che è una fonte comune di confusione. In Python inviate i campi con files= anziché data=, e in PHP passate un array come corpo, così cURL costruisce il multipart.
Posso inviare un messaggio WhatsApp senza template?
Solo all'interno della finestra di assistenza clienti. Quando un cliente vi scrive si apre una finestra di 24 ore in cui potete rispondere con testo, contenuti multimediali o messaggi interattivi. Fuori dalla finestra, o per avviare una conversazione, dovete inviare un template approvato. L'API applica le regole di Meta e un invio in testo libero verso una finestra chiusa non andrà a buon fine.
Come so se il messaggio è stato consegnato?
La chiamata di invio conferma solo che i messaggi sono stati messi in coda e restituisce un batchId. Gli stati per destinatario passano da SUBMITTED, SENT, DELIVERED e READ, oppure terminano come FAILED o REJECTED. Vengono inviati al vostro webhook e potete leggerli anche per batch dall'endpoint bulk-insights.
Posso inviare a molti destinatari in una sola richiesta?
Sì. destinationNo accetta più numeri separati da virgole, punti e virgola o a capo, e destinationFiles accetta un file TXT, CSV o XLSX. Per un template con variabili diverse per destinatario, caricate un file personalizzato con una riga per destinatario. Impostate allowDuplicate su false per rimuovere i numeri duplicati.
Esiste una chiave di idempotenza per evitare invii duplicati?
No. L'API non accetta una chiave di idempotenza. Se una richiesta va in timeout, dal client non potete sapere se sia stata accettata. Registrate ogni invio nel vostro database prima della chiamata, memorizzate il batchId restituito e controllate lo stato o l'elenco dei batch prima di qualsiasi nuovo tentativo.
Serve l'opt-in prima di inviare?
Sì. La policy di Meta richiede che ogni destinatario abbia dato l'opt-in a ricevere messaggi dalla vostra azienda e i template devono essere approvati prima dell'uso. L'API non verifica il consenso al posto vostro. Raccogliete l'opt-in alla fonte, conservate la prova ed escludete chi si disiscrive.
Fonti
Continuate a leggere
Altre guideIntegrazione
Guida ai webhook WhatsApp: eventi, sicurezza e nuovi tentativi
Come funzionano i webhook WhatsApp: eventi di messaggi e stati, gestore affidabile, firma di Meta, nuovi tentativi, idempotenza e recupero degli eventi persi.
10 min di lettura
Integrazione
Inviare SMS via API: esempi in Python, Node.js, PHP e cURL
Inviare SMS con una API in Python, Node.js, PHP e cURL: invii singoli e massivi, Unicode e arabo, conteggio dei segmenti, webhook di stato ed errori.
10 min di lettura
WhatsApp
OTP su WhatsApp o via SMS: costi, consegna e fallback
OTP su WhatsApp e OTP via SMS a confronto: costi, consegna, esperienza utente, template di autenticazione, fallback da WhatsApp a SMS e controlli antifrode.
10 min di lettura
