- Dalla chiamata API alla gestione del flusso applicativo
- Come inserire una API SMS in un workflow applicativo
- Caso 1: Esempio di invio OTP tramite API SMS
- Esempio in Javascript / Node.js
- La stessa logica in Python
- Lo stesso esempio in PHP
- Cosa manca a questi esempi per essere production-ready
- Caso d’uso 2: notifiche transazionali
- Cosa deve fare il codice
- Un primo approccio: invio diretto dal backend
- Quando il flusso cresce: separare l’invio dall’evento
- Nei sistemi legacy o CMS: integrazione lato PHP
- Cosa cambia davvero rispetto all’OTP
- Aspetti critici da gestire
- Conclusione
- Integrare le API SMS in modo affidabile con Esendex
- Esendex in azione
Quando si deve integrare una API SMS all’interno di un sistema, le domande concrete sono sempre le stesse: in quale punto del flusso attivare l’invio, come gestire i tempi di consegna, cosa succede se il messaggio non arriva, come evitare duplicazioni o errori.
La chiamata API in sé è semplice. Il problema è tutto ciò che le sta intorno: la logica applicativa che la attiva, il comportamento asincrono del servizio, la gestione degli stati e degli errori.
È su questi aspetti che si gioca la qualità dell’integrazione. Una soluzione può funzionare dal punto di vista tecnico, ma diventare fragile quando entra nei flussi reali del sistema.
In questo articolo affrontiamo il tema da questa prospettiva, partendo da casi d’uso concreti e vedendo come inserire l’invio SMS all’interno di workflow applicativi reali.
Un’integrazione API SMS nasce sempre da un evento applicativo: un login, un ordine, un errore di sistema. A partire da quell’evento, il backend genera un messaggio e invoca un endpoint esterno.
Da quel momento in poi, il comportamento non è più completamente sotto il controllo dell’applicazione. Il messaggio entra in un flusso asincrono gestito dal gateway SMS e la consegna avviene successivamente, con tempi ed esiti che possono variare.
È proprio questo passaggio, dalla chiamata API alla gestione del ciclo di vita del messaggio, a determinare la qualità reale dell’integrazione.
Dalla chiamata API alla gestione del flusso applicativo
La chiamata API rappresenta il punto di accesso al servizio, ma il comportamento reale dipende da ciò che avviene nel flusso applicativo: la generazione del messaggio, la gestione degli stati asincroni, il trattamento degli errori e le logiche attivate in caso di mancata consegna o ritardi. Sono questi elementi a determinare la solidità dell’integrazione e la sua capacità di funzionare in modo prevedibile anche in condizioni non ideali.
Nel seguito di questo articolo analizziamo due casi d’uso specifici: l’invio di codici OTP per autenticazione e le notifiche transazionali legate a eventi di business.
La scelta non è casuale. Questi due scenari infatti rappresentano tre modalità profondamente diverse di utilizzo delle API SMS e coprono la maggior parte delle implementazioni reali in ambito enterprise.
L’OTP è un caso in cui l’SMS è parte diretta del flusso utente e ha vincoli stringenti di sicurezza e tempestività. Le notifiche transazionali sono legate alla logica di business e richiedono tracciabilità e affidabilità, ma con una maggiore tolleranza operativa.
Analizzare questi due casi consente di evidenziare non solo come cambia l’implementazione tecnica, ma anche come variano le scelte architetturali in funzione del contesto.
Come inserire una API SMS in un workflow applicativo
L’integrazione di una API SMS prende forma all’interno di un flusso applicativo preciso, in cui l’invio del messaggio è solo uno degli elementi della sequenza. L’SMS non nasce “da solo”, ma è sempre la conseguenza di un evento: un’azione dell’utente, una logica di business, una condizione di sistema.
Nella maggior parte dei casi, il processo segue una struttura ricorrente:
- Si verifica un evento applicativo (login, registrazione, ordine completato, errore di sistema)
- Il sistema costruisce il messaggio, spesso in modo dinamico
- Viene effettuata la chiamata all’API SMS
- L’API restituisce una risposta con un identificativo univoco del messaggio (message_id)
- Lo stato del messaggio viene gestito successivamente, fino alla consegna o a un eventuale errore
A prima vista può sembrare un flusso semplice, ma il punto cruciale è un altro: l’invio SMS è un’operazione asincrona. La risposta dell’API indica che il messaggio è stato accettato e preso in carico dal sistema, ma non rappresenta la consegna effettiva.
Da quel momento in poi, il messaggio entra in un processo separato, che coinvolge code, routing e interazione con le reti degli operatori. La consegna avviene in un tempo successivo e con esiti che possono variare: successo, ritardo, mancata consegna.
Caso 1: Esempio di invio OTP tramite API SMS
Una volta generato il codice OTP e associato all’utente, il backend deve costruire una richiesta HTTP verso l’API SMS. In questa fase non stiamo ancora verificando il codice: stiamo solo inviando il messaggio e registrando l’identificativo restituito dal provider.
La struttura logica della chiamata è sempre la stessa, indipendentemente dal linguaggio usato:
- si prepara il payload del messaggio;
- si indica il numero del destinatario;
- si compone il testo dell’SMS;
- si aggiunge un riferimento interno per collegare l’invio al processo applicativo;
- si invia una richiesta
POSTall’endpoint dell’API; - si legge la risposta, in particolare il
message_id.
In un’integrazione reale, il message_id andrebbe salvato nel database insieme al record dell’OTP o della sessione di autenticazione. In questo modo il sistema può correlare l’invio al successivo delivery report o a eventuali errori di recapito. Negli esempi seguenti ci concentriamo sullo step di invio, assumendo che la logica di generazione e storage sia già gestita dal backend.
Esempio in Javascript / Node.js
const axios = require("axios");
async function sendOTP(phoneNumber, otp, userId) {
const response = await axios.post(
"https://api.esempio.com/v1/sms/messages",
{
to: phoneNumber,
body: `Il tuo codice OTP è ${otp}`,
from: "MyApp",
client_reference: `otp_${userId}_${Date.now()}`
},
{
headers: {
Authorization: "Bearer YOUR_API_KEY",
"Content-Type": "application/json"
},
timeout: 5000
}
);
return response.data;
}
In questo esempio la funzione sendOTP riceve tre valori già disponibili nel backend: il numero di telefono, il codice OTP e l’identificativo dell’utente. La funzione non genera il codice e non lo salva: si occupa solo dell’invio.
Il payload contiene quattro campi principali. Il campo to identifica il destinatario, body contiene il testo del messaggio, from definisce il mittente e client_reference permette di collegare l’SMS a un evento interno del sistema. Questo riferimento è utile perché consente di riconoscere l’invio anche quando si analizzano log, report o notifiche di stato.
La chiamata viene eseguita con metodo POST, usando un token di autenticazione nell’header. Il parametro timeout evita che il backend resti bloccato troppo a lungo in attesa della risposta dell’API. In un flusso OTP, infatti, il tempo di risposta è un elemento importante dell’esperienza utente.
La funzione restituisce response.data, cioè il corpo della risposta dell’API. In una implementazione completa, questa risposta dovrebbe essere validata e salvata, soprattutto se contiene un identificativo univoco del messaggio.
La stessa logica in Python
import requests
def send_otp(phone_number, otp, user_id):
response = requests.post(
"https://api.esempio.com/v1/sms/messages",
json={
"to": phone_number,
"body": f"Il tuo codice OTP è {otp}",
"from": "MyApp",
"client_reference": f"otp_{user_id}"
},
headers={
"Authorization": "Bearer YOUR_API_KEY",
"Content-Type": "application/json"
},
timeout=5
)
return response.json()
La versione Python segue lo stesso schema. Cambia la sintassi, ma non cambia l’architettura dell’integrazione: viene costruito un payload JSON, viene inviata una richiesta POST e viene restituita la risposta dell’API.
Questo tipo di implementazione è comune quando l’invio SMS è gestito da un microservizio, da un backend applicativo o da un processo server-side che centralizza tutte le notifiche transazionali.
Anche in questo caso, nella logica reale bisognerebbe aggiungere alcuni controlli:
- verifica dello status code HTTP;
- gestione dei timeout;
- gestione degli errori di rete;
- salvataggio del
message_id; - logging senza esporre il codice OTP in chiaro.
Lo stesso esempio in PHP
<?php
function sendOTP($phoneNumber, $otp, $userId) {
$url = "https://api.esempio.com/v1/sms/messages";
$payload = [
"to" => $phoneNumber,
"body" => "Il tuo codice OTP è " . $otp,
"from" => "MyApp",
"client_reference" => "otp_" . $userId . "_" . time()
];
$headers = [
"Authorization: Bearer YOUR_API_KEY",
"Content-Type: application/json"
];
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode($payload),
CURLOPT_HTTPHEADER => $headers,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 5
]);
$response = curl_exec($ch);
if ($response === false) {
$error = curl_error($ch);
curl_close($ch);
throw new Exception("Errore invio SMS: " . $error);
}
$statusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($statusCode < 200 || $statusCode >= 300) {
throw new Exception("API SMS ha risposto con status code " . $statusCode);
}
return json_decode($response, true);
}
?>
Nel caso PHP la chiamata viene costruita con cURL. Anche qui la logica resta invariata: si definisce l’endpoint, si prepara il payload, si impostano gli header e si invia la richiesta.
La differenza principale è che in PHP la gestione della richiesta HTTP è più esplicita. curl_setopt_array permette di configurare il metodo POST, il corpo della richiesta, gli header, il timeout e il fatto che la risposta debba essere restituita come stringa.
In una versione di produzione sarebbe opportuno aggiungere almeno il controllo di eventuali errori cURL, la lettura dello status code HTTP e la decodifica della risposta JSON. Restituire semplicemente $response può essere sufficiente per un esempio, ma non per un’integrazione robusta.
Cosa manca a questi esempi per essere production-ready
Gli esempi mostrano il punto centrale dell’integrazione, cioè l’invio dell’SMS tramite API. In un contesto reale, però, questo codice rappresenta solo una parte del flusso complessivo e deve essere inserito in una logica applicativa più ampia.
Nel caso di un sistema OTP, il backend deve prima di tutto generare il codice in modo sicuro e gestirne la memorizzazione in modo controllato, spesso evitando di conservarlo in chiaro. A questo si aggiungono vincoli temporali — con una scadenza breve — e meccanismi di protezione come il limite ai tentativi e l’invalidazione immediata dopo l’utilizzo. Anche la gestione dei resend va regolata, per evitare abusi o comportamenti incoerenti.
Sul lato integrazione, è importante salvare il message_id restituito dall’API, così da poter correlare l’invio ai delivery report e monitorare lo stato del messaggio nel tempo. Allo stesso modo, il sistema deve essere in grado di ricevere e interpretare correttamente questi report, integrandoli nei propri flussi di controllo. Infine, tutta la componente di logging deve essere progettata in modo da garantire tracciabilità senza esporre informazioni sensibili come il codice OTP.
In questo quadro, la chiamata API è semplicemente il punto di contatto con il provider SMS. La robustezza dell’integrazione dipende molto di più da come il backend gestisce l’intero ciclo di vita del codice e del messaggio, prima e dopo l’invio.
Caso d’uso 2: notifiche transazionali
Le notifiche transazionali sono uno dei casi più frequenti di integrazione API SMS in ambito applicativo. A differenza dell’OTP, qui il messaggio non serve a completare un flusso di autenticazione, ma a confermare o accompagnare un evento di business: un ordine completato, un pagamento ricevuto, una spedizione in partenza, un appuntamento confermato.
Anche in questo caso il punto di partenza è sempre un evento applicativo. La differenza è che il messaggio non contiene un codice temporaneo da verificare, ma un’informazione operativa che deve arrivare in modo affidabile, coerente e tracciabile.
Un flusso tipico può essere questo:
- il sistema registra un evento di business
- il backend genera il contenuto del messaggio
- viene effettuata la chiamata all’API SMS
- la risposta viene salvata insieme all’identificativo del messaggio
- il sistema tiene traccia dello stato dell’invio
Qui l’attenzione si sposta meno sulla sicurezza del contenuto e più su aspetti come la correlazione con l’evento business, il logging, la gestione degli errori e la possibilità di ricostruire il ciclo di vita del messaggio.
Cosa deve fare il codice
In uno scenario di questo tipo, una buona integrazione dovrebbe almeno:
- ricevere in input i dati dell’evento applicativo
- costruire il testo del messaggio in modo coerente con il contesto
- inviare l’SMS tramite API
- salvare il riferimento dell’invio insieme all’ID dell’evento che lo ha generato
- gestire in modo non bloccante eventuali errori temporanei
Se il sistema è piccolo o monolitico, questa logica può stare direttamente nel backend dell’applicazione. In un’architettura più distribuita, invece, l’invio viene spesso delegato a un servizio separato o a una coda, così da non rallentare il flusso principale.
Un primo approccio: invio diretto dal backend
Nel caso più semplice, il backend riceve l’evento — ad esempio la conferma di un ordine — costruisce il messaggio e chiama direttamente l’API SMS.
In Python, ad esempio, il flusso può essere espresso in modo molto lineare:
import requests
def send_order_confirmation(phone_number, order_id):
message = f"Ordine {order_id} confermato. Riceverai presto i dettagli della spedizione."
response = requests.post(
"https://api.esempio.com/v1/sms/messages",
json={
"to": phone_number,
"body": message,
"from": "MyShop",
"client_reference": f"order_{order_id}"
},
headers={
"Authorization": "Bearer YOUR_API_KEY",
"Content-Type": "application/json"
},
timeout=5
)
return response.json()
In questo esempio il backend fa tre cose essenziali:
costruisce il testo del messaggio a partire dall’evento business
invia l’SMS tramite API
associa il messaggio a un client_reference che richiama l’ordine
Questo approccio è utile quando il flusso è semplice e il volume degli invii è contenuto. Il vantaggio è la chiarezza: la logica resta vicina all’evento che l’ha generata.
Quando il flusso cresce: separare l’invio dall’evento
Quando il sistema comincia a gestire più eventi, più volumi o più tipi di notifica, conviene spesso separare la logica di invio dal flusso principale. In questo caso, invece di chiamare direttamente l’API all’interno del processo che conferma l’ordine, il backend pubblica un evento o inserisce un job in coda. Sarà poi un worker dedicato a generare il messaggio e inviarlo.
In un contesto Node.js, questo approccio è molto naturale, soprattutto se l’applicazione lavora già con code o task asincroni:
const axios = require("axios");
async function processOrderNotification(job) {
const { phoneNumber, orderId } = job;
const message = `Ordine ${orderId} confermato. Riceverai presto i dettagli della spedizione.`;
const response = await axios.post(
"https://api.esempio.com/v1/sms/messages",
{
to: phoneNumber,
body: message,
from: "MyShop",
client_reference: `order_${orderId}`
},
{
headers: {
Authorization: "Bearer YOUR_API_KEY",
"Content-Type": "application/json"
},
timeout: 5000
}
);
return response.data;
}
Qui la logica è la stessa, ma cambia il punto in cui viene eseguita. L’invio non blocca il processo principale e può essere gestito con più controllo: retry, log, priorità, batching.
Nei sistemi legacy o CMS: integrazione lato PHP
In molti casi, soprattutto in ambienti PHP, la notifica transazionale viene ancora generata direttamente dal backend applicativo principale: un e-commerce, un gestionale, un CMS con logica custom.
In questo contesto la priorità è spesso la semplicità di integrazione, purché restino garantiti tracciabilità e controllo dell’errore:
<?php
function sendOrderConfirmation($phoneNumber, $orderId) {
$url = "https://api.esempio.com/v1/sms/messages";
$message = "Ordine " . $orderId . " confermato. Riceverai presto i dettagli della spedizione.";
$payload = [
"to" => $phoneNumber,
"body" => $message,
"from" => "MyShop",
"client_reference" => "order_" . $orderId
];
$headers = [
"Authorization: Bearer YOUR_API_KEY",
"Content-Type: application/json"
];
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode($payload),
CURLOPT_HTTPHEADER => $headers,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 5
]);
$response = curl_exec($ch);
if ($response === false) {
$error = curl_error($ch);
curl_close($ch);
throw new Exception("Errore invio SMS: " . $error);
}
$statusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($statusCode < 200 || $statusCode >= 300) {
throw new Exception("API SMS ha risposto con status code " . $statusCode);
}
return json_decode($response, true);
}
?>
Anche qui il punto centrale non è la sintassi della chiamata, ma il fatto che l’SMS sia agganciato a un evento business riconoscibile e tracciabile.
Cosa cambia davvero rispetto all’OTP
Nel caso delle notifiche transazionali, l’SMS non viene utilizzato per verificare un’azione dell’utente e non chiude un ciclo applicativo. Non c’è un codice da inserire, né una validazione lato backend. Il messaggio è informativo, ma questo non lo rende meno critico.
Il punto si sposta. Non è più la sicurezza del contenuto a essere centrale, ma la qualità del servizio nel suo complesso.
Qui diventano rilevanti altri aspetti. L’affidabilità dell’invio, perché il messaggio deve arrivare in tempi coerenti con l’evento. La tracciabilità, perché ogni SMS deve poter essere ricondotto all’azione che lo ha generato. La coerenza del contenuto, che deve riflettere correttamente lo stato del sistema. E soprattutto la gestione degli errori, che non deve mai compromettere il flusso principale.
Se un OTP non arriva, l’utente non riesce a completare il login. È un errore immediatamente visibile.
Se una notifica transazionale non arriva, il sistema continua a funzionare, ma perde qualità operativa. L’utente non riceve conferme, il customer care aumenta, e diventa più difficile ricostruire cosa è successo.
È per questo che in questi scenari assumono un ruolo centrale elementi come il logging, la correlazione con l’evento business e il monitoraggio degli stati di consegna.
Aspetti critici da gestire
In una implementazione solida, l’invio SMS deve essere sempre collegato a un identificativo interno dell’evento che lo ha generato. Questo permette di mantenere una relazione chiara tra sistema applicativo e messaggistica, ed è fondamentale per qualsiasi attività di debug o audit.
Allo stesso modo, la risposta dell’API — in particolare il message_id — dovrebbe essere salvata e utilizzata per tracciare l’intero ciclo di vita del messaggio. Senza questo passaggio, diventa difficile capire se un SMS è stato inviato, consegnato o fallito.
Un altro punto chiave riguarda il comportamento del sistema in caso di errore. L’invio non dovrebbe mai bloccare il flusso principale: un ordine deve essere confermato anche se l’SMS non parte al primo tentativo. Per questo motivo è comune introdurre meccanismi asincroni, code o worker dedicati, insieme a strategie di retry per gestire errori temporanei.
Infine, nei flussi più critici, è importante monitorare lo stato finale del messaggio. Non basta sapere che la richiesta API è stata accettata: serve visibilità su ciò che accade dopo, fino alla consegna o al fallimento.
In questo senso, la notifica transazionale è il punto in cui l’integrazione API SMS smette di essere una funzione accessoria e diventa parte integrante della qualità percepita del servizio.
Conclusione
I due esempi che abbiamo visto mostrano con chiarezza un punto: l’integrazione di una API SMS non si esaurisce mai nella chiamata a un endpoint.
Nel caso dell’OTP, il tema centrale è la sicurezza del flusso e la gestione controllata del ciclo di vita del codice. Nelle notifiche transazionali, invece, diventano prioritari la tracciabilità, la correlazione con l’evento business e l’affidabilità operativa.
In entrambi i casi, la qualità reale dell’integrazione dipende da ciò che l’applicazione costruisce intorno all’invio: la logica che attiva il messaggio, la gestione degli stati, il trattamento degli errori, il monitoraggio e la capacità di reagire a esiti non ideali.
È qui che l’API smette di essere solo un’interfaccia tecnica e diventa un componente dell’architettura applicativa. Un componente che deve essere progettato in funzione del contesto in cui opera, dei rischi che introduce e del livello di affidabilità richiesto dal sistema.
Per questo motivo, parlare di integrazione API SMS in modo serio significa parlare di workflow, di asincronia, di fault tolerance e di osservabilità. La sintassi della chiamata è solo il punto di partenza. La differenza vera la fa il modo in cui quell’invio viene governato all’interno del sistema.
Integrare le API SMS in modo affidabile con Esendex
Portare le API SMS all’interno di un sistema applicativo richiede più di una semplice integrazione tecnica. Significa costruire flussi affidabili, gestire correttamente l’asincronia, garantire tracciabilità e mantenere il controllo anche in condizioni non ideali.
La piattaforma Esendex è progettata per supportare questo tipo di integrazioni, offrendo API REST stabili, infrastruttura scalabile e strumenti avanzati di monitoraggio e gestione degli invii. Questo consente di inserire l’SMS nei flussi applicativi con un livello di affidabilità adeguato anche nei contesti più critici.
Se stai lavorando a un’integrazione API SMS o vuoi migliorare un sistema già esistente, il team Esendex può supportarti nella progettazione e nell’implementazione, aiutandoti a definire l’architettura più adatta e a gestire correttamente tutti gli aspetti operativi.
Puoi contattare gli esperti per approfondire il tuo caso specifico e valutare la soluzione più efficace per il tuo contesto.
Esendex in azione
Scopri tutte le potenzialità della messaggistica mobile con Esendex.
Richiedi subito una demo a uno dei nostri esperti.