1. Concetti generali
L'integrazione avviene tramite una singola API REST. Il gestionale invia una richiesta HTTP ogni volta che un paziente deve ricevere un messaggio WhatsApp; IntelFriends si occupa della consegna effettiva attraverso l'infrastruttura WhatsApp Business.
- Protocollo: HTTPS esclusivamente. Le richieste in HTTP vengono rifiutate.
- Formato: JSON, sia in richiesta sia in risposta.
- Modello: asincrono. La richiesta viene accettata e messa in coda; l'invio effettivo avviene a breve giro, non all'interno della stessa risposta HTTP.
- Ogni gestionale partner riceve credenziali dedicate, distinte da quelle di altri gestionali.
2. Autenticazione
Ogni richiesta deve includere una API key nell'header HTTP Authorization, in formato Bearer token.
Authorization: Bearer if_live_xxxxxxxxxxxx_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
La API key identifica univocamente il gestionale partner: non è necessario indicare il nome del gestionale nel corpo della richiesta. La chiave viene fornita da IntelFriends al momento dell'attivazione ed è revocabile su richiesta o in caso di necessità.
Una chiave può operare soltanto sui clienti finali del proprio gestionale. Una richiesta che indica un client_ref appartenente a un altro gestionale viene respinta con 403.
3. Identificazione del cliente finale
Ogni richiesta deve indicare a quale cliente finale — cioè a quale studio o clinica — si riferisce il messaggio, tramite il campo client_ref.
client_refè l'identificativo che il gestionale utilizza internamente per quel cliente: non è necessario adottare un nuovo sistema di codici.- IntelFriends mantiene la mappatura fra
client_refe la configurazione tecnica necessaria all'invio, che resta interamente a nostro carico. - Un
client_refnon configurato fa respingere la richiesta con404: nessun cliente viene creato automaticamente. - Un cliente sospeso fa respingere la richiesta con
403.
client_ref va concordato prima del primo invio.
4. Invio di un messaggio
Endpoint
POST https://api.intelfriends.com/v1/messages
Header
Authorization: Bearer if_live_xxxxxxxxxxxx_xxxxxxxxxxxxxxxxxxxxxxxxxxx Content-Type: application/json Idempotency-Key: 7c1b2e44-9f3a-4d2e-8b1a-2f6e9c0d1a5b
Esempio di richiesta
{
"client_ref": "STUDIO_ROSSI_MILANO",
"recipient": "+393331234567",
"template": "m01_promemoria_richiesta_conferma",
"variables": {
"elenco_appuntamenti": "Mario Rossi, 07 Agosto 2026 alle 10:30",
"indirizzo_completo": "Via Roma 12, Milano",
"dettagli_aggiuntivi": "Arrivare 10 minuti prima",
"link_conferma": "https://gestionale.esempio.it/c/8f3k2"
}
}
Esempio di risposta 202 Accepted
{
"message_id": "msg_H7y2HKTZLz0PIYWd",
"status": "queued"
}
Il message_id è il riferimento univoco del messaggio: va conservato per consultarne lo stato (sezione 8) e per qualsiasi segnalazione al supporto.
| Campo | Obbligatorio | Descrizione |
|---|---|---|
client_ref | sì | Il cliente finale a cui si riferisce il messaggio. |
recipient | sì | Numero del paziente in formato internazionale E.164, con il + iniziale. Un formato diverso viene respinto con 400 invalid_recipient. |
template | sì | Nome del messaggio, fra quelli elencati alla sezione 6. |
variables | dipende | Oggetto con le variabili richieste dal messaggio scelto. |
5. Variabili disponibili
Le variabili si passano come oggetto nominato, non come elenco posizionale: l'integrazione resta leggibile e non si rompe se in futuro cambia l'ordine dei segnaposti nel testo approvato.
| Variabile | Tipo | Descrizione | Esempio |
|---|---|---|---|
elenco_appuntamenti | stringa libera | Gli appuntamenti da citare nel messaggio, già scritti come devono comparire. | "Mario Rossi, 07 Agosto 2026 alle 10:30" |
indirizzo_completo | stringa | Indirizzo dello studio, mostrato nel messaggio. | "Via Roma 12, Milano" |
dettagli_aggiuntivi | stringa | Nota libera aggiunta in coda al corpo del messaggio. | "Arrivare 10 minuti prima" |
link_conferma | URL https | Indirizzo aperto dal bottone Confermo. | ".../c/8f3k2" |
link_recensione | URL https | Indirizzo aperto dal bottone della recensione. | ".../recensione" |
link_anamnesi | URL https | Indirizzo del modulo di anamnesi da compilare prima della visita. | ".../anamnesi/8f3k2" |
nome | stringa | Nome del paziente, usato nel saluto di apertura. È facoltativa su tutti i messaggi: se non viene fornita, al suo posto compare la parola paziente, quindi il messaggio si apre con "Gentile paziente". | "Mario" |
Formato di elenco_appuntamenti
È una stringa libera, non un elenco strutturato: viene inserita nel testo del messaggio così com'è. Può descrivere anche più appuntamenti nella stessa stringa, ad esempio "Mario Rossi alle 10:00 e Luigi Verdi alle 11:00". La formattazione della data è quindi a carico del gestionale, che è l'unico a sapere come vanno raggruppati e presentati gli appuntamenti di quel paziente.
Ogni variabile il cui nome comincia per link_ deve essere un indirizzo https valido: vale per quelle elencate qui sopra e per qualsiasi altra venga aggiunta in futuro. Un indirizzo http o malformato fa respingere la richiesta con 400.
Le variabili obbligatorie mancanti, vuote o composte di soli spazi fanno respingere la richiesta con 400 missing_variables, con l'elenco di quanto manca.
Le variabili non previste dal messaggio scelto vengono ignorate, non causano errore: è possibile inviare sempre lo stesso insieme di dati cambiando solo il nome del messaggio.
6. Messaggi disponibili
Il campo template deve contenere uno dei nomi elencati qui sotto. L'elenco è concordato fra IntelFriends e il gestionale, ed è lo stesso per tutti i clienti finali di quel gestionale: un nome che non vi compare fa respingere la richiesta con 400 template_not_found, prima che il messaggio entri in coda.
Ogni messaggio richiede solo il sottoinsieme di variabili indicato. I bottoni sono elementi strutturati del template WhatsApp, distinti dal testo del corpo: non vanno inseriti nel messaggio, basta fornire il link a cui puntano.
elenco_appuntamenti indirizzo_completodettagli_aggiuntivi nomeGentile nome, le confermiamo i dettagli della sua prenotazione:
elenco_appuntamenti
📍 Ci trova in indirizzo_completo
dettagli_aggiuntivi
A presto
elenco_appuntamenti indirizzo_completodettagli_aggiuntivi nomeGentile nome, questo è un promemoria per ricordarle i dettagli della sua prenotazione:
elenco_appuntamenti
📍 Ci trova in indirizzo_completo
dettagli_aggiuntivi
A presto
elenco_appuntamenti indirizzo_completo link_confermadettagli_aggiuntivi nomeGentile nome,
questo è un promemoria per ricordarle che la attendiamo in studio secondo il seguente programma:
elenco_appuntamenti
📍 Ci trova in indirizzo_completo
La preghiamo di confermare la sua presenza cliccando sul pulsante in basso.
dettagli_aggiuntivi
A presto
elenco_appuntamenti indirizzo_completo link_anamnesidettagli_aggiuntivi nomeGentile nome,
questo è un promemoria per ricordarle che la attendiamo in studio secondo il seguente programma:
elenco_appuntamenti
📍 Ci trova in indirizzo_completo
La vostra anamnesi risulta scaduta e vi invitiamo alla compilazione prima di arrivare in studio.
dettagli_aggiuntivi
A presto
link_recensionenomeGentile nome, rimaniamo a disposizione per qualsiasi domanda sul post trattamento.
La sua recensione su Google aiuterebbe altre persone a trovarci. Grazie!
elenco_appuntamenti link_confermadettagli_aggiuntivi nomeGentile nome,
le ricordiamo i dettagli del suo appuntamento d'igiene orale in programma:
elenco_appuntamenti
La preghiamo di confermare la sua presenza cliccando sul pulsante in basso.
dettagli_aggiuntivi
La ringrazio e Le auguro una buona giornata.
nomeGentile nome,
grazie per aver confermato la sua presenza
nomeGentile nome,
Per poterle offrire il miglior servizio, la preghiamo di confermare l'appuntamento.
Non porta bottone: è un semplice sollecito e rimanda alla richiesta di conferma già inviata al paziente, dove il bottone c'è. Va quindi usato dopo m01_promemoria_richiesta_conferma, non al suo posto.
Come cambia l'elenco
L'elenco non è congelato: può essere ampliato con messaggi nuovi e una voce può essere ritirata quando non serve più.
- Un messaggio nuovo si concorda con IntelFriends definendo nome, variabili obbligatorie e facoltative ed eventuali bottoni. Dal momento dell'attivazione è utilizzabile senza alcun intervento sull'integrazione esistente: basta indicarne il nome nel campo
template. - Un messaggio ritirato resta leggibile nello storico, ma le nuove richieste che lo utilizzano vengono respinte con
400 template_deprecated. Il ritiro viene comunicato con anticipo. - L'elenco vale per tutti i clienti finali del gestionale: non esistono elenchi diversi per singolo studio.
I testi qui riportati sono quelli approvati secondo le regole WhatsApp. Una modifica del testo comporta una nuova approvazione e viene comunicata prima di entrare in vigore.
7. Idempotenza
Per evitare invii duplicati in caso di timeout, retry automatici, errori di rete o doppia esecuzione di una procedura, ogni richiesta può includere un header Idempotency-Key con un valore univoco per il singolo messaggio: si consiglia un UUID v4, generato dal gestionale.
Idempotency-Key: 7c1b2444-4f13-4a90-8b3e-861e2fe5e93e
L'header è facoltativo, ma fortemente consigliato: senza, due richieste identiche generano due messaggi e il paziente li riceve entrambi.
- Stessa chiave, stesso corpo: viene restituita la risposta originale, con lo stesso
message_id, senza generare un secondo messaggio. La risposta riporta l'headerIdempotent-Replay: true. - Stessa chiave, corpo diverso: la richiesta viene rifiutata con
409. È il segnale che quella chiave è già stata usata per un altro messaggio. - Le chiavi sono conservate per 48 ore; oltre tale periodo lo stesso valore può essere riutilizzato.
8. Stato dei messaggi e consuntivi
Stato di un singolo messaggio
GET https://api.intelfriends.com/v1/messages/{message_id}
{
"message_id": "msg_H7y2HKTZLz0PIYWd",
"client_ref": "STUDIO_ROSSI_MILANO",
"template_name": "promemoria_richiesta_conferma",
"recipient": "+393331234567",
"status": "delivered",
"attempts": 1,
"error_code": null,
"error_message": null,
"created_at": "2026-08-09T09:12:31.089Z",
"sent_at": "2026-08-09T09:12:31.112Z",
"delivered_at": "2026-08-09T09:12:34.470Z"
}
La consultazione è possibile solo per i messaggi del proprio gestionale: un message_id altrui o inesistente risponde 404.
| Stato | Significato |
|---|---|
queued | Accettato e in attesa di invio. |
sending | Invio in corso. |
sent | Consegnato all'infrastruttura di messaggistica. sent_at riporta il momento. |
delivered | Recapitato al telefono del paziente. delivered_at riporta il momento. |
read | in arrivoAperto dal paziente. |
failed | Invio non riuscito. error_code ed error_message riportano il motivo. |
Lo stato avanza sempre in avanti e non torna mai indietro: una volta registrata la consegna, un'informazione più vecchia che arriva in ritardo non la cancella.
Il passaggio da sent a delivered non è immediato: la propagazione della conferma richiede almeno 5 minuti. Un messaggio ancora sent entro quella finestra è del tutto normale e non va segnalato né rimandato.
failed ha già esaurito i tentativi automatici previsti. Un nuovo invio è una richiesta nuova, con una Idempotency-Key nuova: ripetere la precedente restituirebbe la risposta originale senza inviare nulla.
Consuntivo
GET https://api.intelfriends.com/v1/usage?client_ref=...&from=2026-08-01&to=2026-09-01
{
"partner": "gestionale",
"from": "2026-08-01",
"to": "2026-09-01",
"conteggio_su": "accettati",
"righe": [
{ "client_ref": "STUDIO_ROSSI_MILANO", "template_name": "conferma",
"status": "sent", "messaggi": 128, "fatturabili": 128 }
]
}
Restituisce il conteggio dei messaggi per cliente finale, messaggio e stato nel periodo indicato. I tre parametri sono facoltativi: senza client_ref il conteggio copre tutti i clienti del gestionale, senza date copre l'intero storico. L'intervallo include from ed esclude to.
9. Codici di risposta
| Codice | Stato | Significato |
|---|---|---|
| 202 | Accepted | Richiesta ricevuta e messa in coda. L'invio effettivo avviene a breve. |
| 400 | Bad Request | Payload non valido. Il campo error.code distingue il motivo: missing_variables, invalid_recipient, template_not_found, template_deprecated, invalid_payload. |
| 401 | Unauthorized | API key assente, non valida o revocata. |
| 403 | Forbidden | Il client_ref indicato non appartiene al gestionale associato alla chiave, oppure il cliente è sospeso. |
| 404 | Not Found | client_ref non configurato, oppure message_id inesistente. |
| 409 | Conflict | Idempotency-Key già utilizzata con un payload differente. |
| 429 | Too Many Requests | Limite superato. L'header Retry-After indica dopo quanti secondi riprovare. Il campo error.code distingue i due casi: rate_limited è un limite al minuto, limite_giornaliero_clinica è il tetto di avvio di uno studio appena attivato (sezione 10). |
| 500 | Internal Server Error | Errore lato IntelFriends. Sicuro da ritentare con la stessa Idempotency-Key. |
In caso di errore il corpo della risposta include un campo error con codice e messaggio leggibile. Il codice è pensato per essere gestito dal programma, il messaggio per essere letto da una persona:
{
"error": {
"code": "missing_variables",
"message": "Variabili obbligatorie mancanti: link_conferma.",
"missing": ["link_conferma"]
}
}
I codici 4xx segnalano un problema nella richiesta e non vanno ritentati identici: la richiesta verrebbe respinta allo stesso modo. I 5xx vanno ritentati, riusando la stessa Idempotency-Key.
10. Limiti di frequenza
Le API sono soggette a limiti di frequenza su due livelli, entrambi al minuto: per API key e per singolo client_ref. Il secondo livello garantisce che un singolo studio in condizione anomala non consumi la capacità assegnata a tutti gli altri.
A questi se ne aggiunge un terzo solo per gli studi appena attivati: un tetto sul numero di pazienti diversi contattabili nelle 24 ore. Non è un limite IntelFriends, è la soglia che WhatsApp applica a un numero di telefono appena acceso. Decade da solo a una data stabilita in fase di attivazione e non riguarda gli studi già a regime.
Come si leggono dalla risposta
Ogni risposta riporta lo stato di tutti i limiti che si applicano a quella richiesta: due sull'invio di un messaggio, tre se lo studio è nel periodo di avvio, uno sulle chiamate di sola lettura. Il nome del limite sta nell'header, così si legge direttamente quello che interessa senza doverlo interpretare.
| Header | Cosa riporta |
|---|---|
X-RateLimit-Gestionale-LimitX-RateLimit-Gestionale-RemainingX-RateLimit-Gestionale-Reset |
Il limite della vostra API key: quante richieste al minuto, quante ne restano, fra quanti secondi riparte il conteggio. Presente su tutte le chiamate. |
X-RateLimit-Clinica-LimitX-RateLimit-Clinica-RemainingX-RateLimit-Clinica-Reset |
Lo stesso, per il singolo client_ref indicato nella richiesta. Presente sull'invio di un messaggio. |
X-RateLimit-Giornaliero-LimitX-RateLimit-Giornaliero-RemainingX-RateLimit-Giornaliero-ResetX-RateLimit-Giornaliero-Fino-Al |
Presente solo per gli studi nel periodo di avvio. Remaining sono pazienti diversi ancora contattabili nelle 24 ore, non richieste: un secondo messaggio a chi è già stato contattato oggi passa e non consuma niente. Reset dice fra quanti secondi si libera il primo posto — la finestra è mobile, i posti tornano liberi uno alla volta. Fino-Al è la data in cui il tetto decade. |
Oltre a questi, tre header senza nome — X-RateLimit-Limit, X-RateLimit-Remaining e X-RateLimit-Reset — riportano il limite che si esaurirà per primo, cioè quello che fermerà la richiesta successiva, e X-RateLimit-Vincolante dice quale dei tre è. A chi vuole rallentare da solo prima di essere respinto bastano questi quattro.
- Al superamento la risposta è
429con headerRetry-Afterin secondi. Gli header dei limiti sono presenti anche sulla risposta respinta. - I due
429vanno distinti dal campoerror.code: surate_limitedsi riprova entro il minuto, sulimite_giornaliero_clinicasi riprende dopo ore. In entrambi i casiRetry-Afterdice quanto aspettare. - I valori in vigore vengono comunicati in fase di attivazione. Sono stabiliti per singolo gestionale — entrambi i livelli al minuto — e possono essere modificati su richiesta, con effetto immediato e senza interruzioni.
11. Sicurezza
- Tutte le richieste devono avvenire esclusivamente via HTTPS.
- La API key non va mai inclusa nell'URL o nei parametri in query, ma solo nell'header
Authorization. - Si raccomanda la rotazione periodica della chiave. IntelFriends emette la nuova chiave prima di revocare la precedente, mantenendo la continuità del servizio.
- Nel campo
dettagli_aggiuntivinon vanno inseriti dati sanitari oltre a quanto strettamente necessario al messaggio: è testo che finisce su WhatsApp, sul telefono del paziente. - I dati dei pazienti trasmessi vengono trattati esclusivamente per la finalità dell'invio e conservati per il tempo necessario all'invio e al supporto tecnico.
12. Evoluzioni previste
Le seguenti funzionalità non fanno parte della versione attuale e potranno essere aggiunte in versioni successive:
- Webhook di notifica verso il gestionale al cambio di stato di un messaggio, in alternativa alla consultazione descritta alla sezione 8.
- Tracciamento delle interazioni sui bottoni di conferma e modifica.
- Ricezione dei messaggi inviati dal paziente in risposta.
- Allegati e contenuti multimediali.
Gli aggiornamenti a questo documento sono numerati in modo incrementale e comunicati al team tecnico del gestionale partner.