WhatsApp API
Cos'è WhatsApp Business API
WhatsApp Business API consente di inviare messaggi tramite la piattaforma WhatsApp Business di Meta attraverso le API Qlara. È il canale di messaggistica più utilizzato al mondo, con elevati tassi di apertura e coinvolgimento.
I messaggi possono essere inviati in due modalità:
- Template: messaggi basati su template approvati da Meta. Possono essere inviati in qualsiasi momento, senza limiti temporali. Ideali per notifiche, marketing e comunicazioni proattive.
- Messaggio libero (body): contenuto personalizzato (testo, immagini, documenti, ecc.). Utilizzabile solo entro la finestra di 24 ore dall'ultimo messaggio ricevuto dall'utente.
:::warning Finestra di 24 ore I messaggi liberi (body) possono essere inviati solo entro 24 ore dall'ultimo messaggio ricevuto dall'utente. Dopo questo periodo, è necessario utilizzare un template approvato da Meta. I messaggi inviati al di fuori della finestra verranno rifiutati. :::
Il sistema supporta una catena di fallback automatica: se WhatsApp non può consegnare il messaggio, il sistema prova con RCS e infine con SMS.
Endpoint
Invio messaggi
POST https://lora-api.agiletelecom.com/api/message-server/whatsapp/send
Numeri di telefono
GET https://lora-api.agiletelecom.com/api/message-server/whatsapp/phone-numbers
Content-Type: application/json
Autenticazione: API Key (X-Api-Key) oppure Basic Auth (Authorization: Basic)
Campi della richiesta di invio
| Campo | Tipo | Obbligatorio | Default | Descrizione |
|---|---|---|---|---|
destination | string | Sì | – | Numero di telefono destinatario in formato internazionale (es. +393401234567) |
phoneNumberId | integer | Sì | – | ID del numero WhatsApp Business mittente (ottenuto da Get All Phone Numbers) |
template | object | Sì* | – | Riferimento a un template approvato da Meta. *Mutuamente esclusivo con body |
body | object | Sì* | – | Contenuto del messaggio libero. *Mutuamente esclusivo con template |
campaignId | string | No | – | Identificativo della campagna per raggruppare più messaggi. Massimo 255 caratteri |
messageId | string | No | (auto) | Identificativo personalizzato del messaggio. Se non fornito, viene generato automaticamente (UUID) |
simulation | boolean | No | false | Se true, il messaggio viene elaborato ma non inviato fisicamente. Utile per test |
enableNotification | boolean | No | true | Se true, abilita le notifiche di consegna e lettura verso il tuo URL di callback |
placeholders | object | No | – | Mappa chiave-valore per sostituire i placeholder nel template. Es: {"nome": "Mario"} sostituisce {nome} nel messaggio |
scheduledDate | string | No | – | Data e ora di invio programmato. Formato: yyyy-MM-dd HH:mm:ss.SSSZ (es. 2025-10-01 09:00:00.000+0000) |
fallbackRcs | object | No | – | Configurazione fallback su canale RCS (vedi sezione Fallback) |
fallbackSms | object | No | – | Configurazione fallback su canale SMS. Obbligatorio se fallbackRcs è presente |
Oggetto template
:::tip Pre-approvazione template I template devono essere approvati da Meta prima dell'uso. Pianifica in anticipo: il processo di approvazione può richiedere fino a 24 ore. Controlla lo stato dei tuoi template nella piattaforma Qlara. :::
Usato per inviare un messaggio basato su un template approvato da Meta.
| Campo | Tipo | Obbligatorio | Descrizione |
|---|---|---|---|
id | integer | Sì | ID del template nel sistema (ottenuto da Get All Templates) |
mediaUrl | string | No | URL del media da inserire nell'header del template (se il template prevede un header con immagine o video) |
Oggetto body
Usato per inviare un messaggio libero. Deve contenere uno e uno solo dei seguenti sotto-oggetti. I messaggi liberi possono essere inviati solo entro la finestra di 24 ore dall'ultimo messaggio ricevuto dall'utente.
body > text
Messaggio di testo semplice.
| Campo | Tipo | Obbligatorio | Descrizione |
|---|---|---|---|
body | string | Sì | Testo del messaggio |
body > image
Immagine con didascalia opzionale.
| Campo | Tipo | Obbligatorio | Descrizione |
|---|---|---|---|
url | string | Sì* | URL pubblico dell'immagine. *Mutuamente esclusivo con key |
key | string | Sì* | Chiave media su storage interno. *Mutuamente esclusivo con url |
caption | string | No | Didascalia visualizzata sotto l'immagine |
body > video
Video con didascalia opzionale.
| Campo | Tipo | Obbligatorio | Descrizione |
|---|---|---|---|
url | string | Sì* | URL pubblico del video. *Mutuamente esclusivo con key |
key | string | Sì* | Chiave media interna. *Mutuamente esclusivo con url |
caption | string | No | Didascalia |
body > audio
Messaggio audio.
| Campo | Tipo | Obbligatorio | Descrizione |
|---|---|---|---|
url | string | Sì* | URL pubblico dell'audio. *Mutuamente esclusivo con key |
key | string | Sì* | Chiave media interna. *Mutuamente esclusivo con url |
body > document
Documento (PDF, DOC, ecc.) con nome file e didascalia opzionali.
| Campo | Tipo | Obbligatorio | Descrizione |
|---|---|---|---|
url | string | Sì* | URL pubblico del documento. *Mutuamente esclusivo con key |
key | string | Sì* | Chiave media interna. *Mutuamente esclusivo con url |
filename | string | No | Nome del file mostrato al destinatario (es. fattura_marzo.pdf) |
caption | string | No | Didascalia |
body > sticker
Sticker in formato WebP.
| Campo | Tipo | Obbligatorio | Descrizione |
|---|---|---|---|
url | string | Sì* | URL pubblico dello sticker (formato WebP). *Mutuamente esclusivo con key |
key | string | Sì* | Chiave media interna. *Mutuamente esclusivo con url |
body > location
Posizione geografica visualizzata su mappa.
| Campo | Tipo | Obbligatorio | Descrizione |
|---|---|---|---|
latitude | string | Sì | Latitudine (es. "45.4642") |
longitude | string | Sì | Longitudine (es. "9.1900") |
name | string | No | Nome della posizione (es. "Negozio Milano Centro") |
address | string | No | Indirizzo testuale (es. "Via Roma 1, 20121 Milano") |
body > reaction
Reazione emoji a un messaggio esistente.
| Campo | Tipo | Obbligatorio | Descrizione |
|---|---|---|---|
messageId | string | Sì | ID del messaggio a cui reagire |
emoji | string | Sì | Emoji della reazione |
Catena di fallback
Il sistema supporta l'invio con catena di fallback automatica:
WhatsApp → RCS → SMS
Se WhatsApp non può consegnare il messaggio, il sistema prova con RCS (se configurato) e infine con SMS.
Oggetto fallbackRcs
| Campo | Tipo | Obbligatorio | Descrizione |
|---|---|---|---|
agentId | integer | Sì | ID dell'agente RCS mittente |
templateId | integer | Sì* | ID template RCS. *Mutuamente esclusivo con body |
type | string | No | Tipo body RCS: TEXT, CARD, CAROUSEL (richiesto se si usa body) |
body | object | Sì* | Body RCS inline. *Mutuamente esclusivo con templateId |
Oggetto fallbackSms
| Campo | Tipo | Obbligatorio | Descrizione |
|---|---|---|---|
sender | string | Sì | Mittente dell'SMS (alfanumerico o numerico) |
text | string | Sì | Testo dell'SMS di fallback |
Regola: Se
fallbackRcsè presente,fallbackSmsè obbligatorio. È possibile configurare solofallbackSmssenzafallbackRcsper un fallback diretto WhatsApp -> SMS.
Link tracciati
Per monitorare i click sui link nei template, puoi utilizzare il placeholder shortLinkT1 nel campo placeholders:
"placeholders": {
"shortLinkT1": "https://www.example.com/landing-page"
}
Il sistema genera automaticamente un short link tracciato che sostituisce il placeholder {shortLinkT1} nel template. Questo permette di monitorare quanti destinatari hanno cliccato il link.
Risposta
{
"messageId": "e76614d1-4ac1-4d94-89f0-d07f1b5a190c",
"simulation": false,
"results": {
"whatsapp": {
"accepted": true
},
"rcs": null,
"sms": null
}
}
| Campo | Tipo | Descrizione |
|---|---|---|
messageId | string | ID univoco del messaggio. Usalo per correlare le notifiche di consegna |
simulation | boolean | true se il messaggio era in modalità simulazione |
results.whatsapp.accepted | boolean | true se WhatsApp ha accettato il messaggio |
results.rcs | object/null | Risultato fallback RCS (se configurato, altrimenti null) |
results.sms | object/null | Risultato fallback SMS (se configurato, altrimenti null) |
Importante:
accepted: truesignifica che il messaggio è stato accettato per l'invio, non che è stato consegnato. La conferma di consegna arriva tramite callback.
Notifiche (Callback)
Delivery Notification
Notifica di consegna del messaggio:
{
"channel": "WHATSAPP",
"eventType": "DELIVERY",
"messageId": "88116e2f-b8b4-40fe-b727-f453b6b99adc",
"destination": "+393409997528",
"statusCode": 3,
"description": "delivered",
"eventDate": "2025-10-16T10:42:18Z"
}
| statusCode | Significato |
|---|---|
3 | Messaggio consegnato |
6 | Messaggio non consegnabile |
Read Notification
Notifica di lettura (il destinatario ha aperto il messaggio):
{
"channel": "WHATSAPP",
"eventType": "READ",
"messageId": "88116e2f-b8b4-40fe-b727-f453b6b99adc",
"destination": "+393409997528",
"eventDate": "2025-10-16T10:42:18Z"
}
Inbound Text Message
Notifica quando l'utente risponde con un messaggio di testo:
{
"channel": "WHATSAPP",
"eventType": "INBOUND",
"messageId": "wamid.HBxxxxxxxxxxxxxxxxx",
"source": "+393471488489",
"destination": "+393209998877",
"receivedDate": "2025-10-31T10:59:05.099Z",
"messageType": "TEXT",
"text": "Messaggio di risposta dell'utente"
}
Inbound Media Message
Notifica quando l'utente invia un'immagine, audio o video. Il campo mediaKey contiene la chiave per scaricare il file.
{
"channel": "WHATSAPP",
"eventType": "INBOUND",
"source": "+393471488489",
"destination": "+393209998877",
"receivedDate": "2025-10-31T11:00:41.003Z",
"messageType": "IMAGE",
"mediaKey": "b394ab72/efd4987c/39b4879f6fed01f0d622453be1488c93"
}
Numeri di telefono (Phone Numbers)
Per ottenere la lista dei numeri WhatsApp Business associati al tuo account:
GET https://lora-api.agiletelecom.com/api/message-server/whatsapp/phone-numbers
Dalla risposta, prendi il valore del campo id del numero che vuoi usare come mittente: sarà il phoneNumberId da inserire nelle richieste di invio.
Per ottenere i dettagli di un singolo numero:
GET https://lora-api.agiletelecom.com/api/message-server/whatsapp/phone-numbers/{id}
Esempi
1. Invio con template
Invio di un messaggio basato su un template approvato, con placeholder e media nell'header:
{
"destination": "+393401234567",
"phoneNumberId": 5,
"template": {
"id": 42,
"mediaUrl": "https://example.com/images/promo.jpg"
},
"placeholders": {
"nome": "Mario",
"codiceSconto": "SPRING25"
},
"enableNotification": true
}
2. Invio messaggio di testo
Invio di un messaggio di testo libero (richiede finestra 24h aperta):
{
"destination": "+393401234567",
"phoneNumberId": 5,
"body": {
"text": {
"body": "Ciao Mario! Grazie per averci contattato. Il tuo ordine è in fase di preparazione."
}
}
}
3. Invio immagine
Invio di un'immagine con didascalia:
{
"destination": "+393401234567",
"phoneNumberId": 5,
"body": {
"image": {
"url": "https://example.com/images/ricevuta-ordine.jpg",
"caption": "Ecco la ricevuta del tuo ordine #12345"
}
}
}
4. Invio documento
Invio di un documento PDF con nome file personalizzato:
{
"destination": "+393401234567",
"phoneNumberId": 5,
"body": {
"document": {
"url": "https://example.com/docs/fattura-marzo-2025.pdf",
"filename": "Fattura_Marzo_2025.pdf",
"caption": "In allegato la fattura di marzo 2025"
}
},
"campaignId": "fatture-marzo-2025"
}
5. Invio con fallback RCS + SMS
Invio con catena di fallback completa: WhatsApp -> RCS -> SMS. Se WhatsApp non riesce a consegnare, il sistema prova con RCS; se anche RCS fallisce, invia un SMS.
{
"destination": "+393401234567",
"phoneNumberId": 5,
"template": {
"id": 42
},
"placeholders": {
"nome": "Mario"
},
"enableNotification": true,
"fallbackRcs": {
"agentId": 1,
"templateId": 10
},
"fallbackSms": {
"sender": "MyBrand",
"text": "Ciao Mario, abbiamo una promozione per te! Visita il nostro sito per i dettagli."
}
}
Risposta (con fallback attivato):
{
"messageId": "e76614d1-4ac1-4d94-89f0-d07f1b5a190c",
"simulation": false,
"results": {
"whatsapp": {
"accepted": true
},
"rcs": {
"accepted": true,
"reasons": []
},
"sms": {
"accepted": true,
"unicode": false,
"parts": 1,
"reasons": []
}
}
}
Codici di errore
| Codice HTTP | Significato | Cosa fare |
|---|---|---|
200 | Richiesta elaborata | Controlla results.whatsapp.accepted per verificare l'accettazione |
400 | Richiesta non valida | Verifica i campi obbligatori (destination, phoneNumberId, body o template) |
401 | Non autenticato | Verifica la tua API Key o le credenziali Basic Auth |
403 | Accesso negato | IP non in whitelist o risorsa non autorizzata |
404 | Non trovato | Template o numero non esistente |
422 | Entità non processabile | Dati sintatticamente corretti ma semanticamente non validi (es. template non approvato) |
429 | Troppe richieste | Hai superato il rate limit. Riprova dopo il periodo indicato |
500 | Errore interno del server | Riprova più tardi. Se il problema persiste, contatta il supporto |