SMS in entrata
Cos'è l'SMS in entrata
Noleggia un numero di telefono dedicato per ricevere tutti gli SMS in arrivo in tempo reale. Agile Telecom ospita la SIM e inoltra i messaggi tramite email o webhook -- ideale per linee di assistenza clienti, promemoria appuntamenti o risposte automatiche.
Consegna tramite email
Quando la consegna tramite email è configurata, gli SMS in arrivo vengono inoltrati al tuo indirizzo email specificato da smsin@agiletelecom.com.
Ogni email contiene i seguenti campi:
| Campo | Descrizione |
|---|---|
| From | Numero di telefono del mittente (MSISDN) |
| To | Il tuo numero dedicato |
| Date/Time | Timestamp di ricezione del messaggio |
| Body | Contenuto testuale dell'SMS |
:::tip Configurazione semplice La consegna tramite email non richiede sviluppo -- basta configurare il tuo indirizzo email di destinazione nel portale e iniziare a ricevere i messaggi immediatamente. :::
Webhook HTTP POST
Per l'elaborazione programmatica, configura un webhook HTTP POST. Agile Telecom invierà i dati degli SMS in arrivo al tuo endpoint.
Parametri della richiesta
| Parametro | Tipo | Descrizione |
|---|---|---|
originator | string | Numero di telefono del mittente (MSISDN) |
destination | string | Il tuo numero dedicato |
date_time | string | Timestamp di ricezione del messaggio |
text | string | Contenuto testuale dell'SMS |
Requisiti della risposta
Il tuo endpoint deve rispondere con +OK nel corpo della risposta. Qualsiasi altra risposta viene considerata un errore.
| Impostazione | Valore |
|---|---|
| Risposta richiesta | +OK |
| Timeout | 30 secondi |
| Ritardo tra i tentativi | 15 minuti |
| Tentativi massimi | 3 |
:::warning Risposta obbligatoria
Se il tuo endpoint non risponde con +OK entro 30 secondi, il sistema effettuerà fino a 3 tentativi con un intervallo di 15 minuti tra un tentativo e l'altro. Dopo 3 fallimenti, il messaggio viene scartato.
:::
Esempi di implementazione del webhook
Python (Flask)
from flask import Flask, request
import threading
app = Flask(__name__)
def process_message(originator, destination, date_time, text):
"""Process inbound SMS asynchronously."""
print(f"From: {originator}, To: {destination}, Time: {date_time}")
print(f"Message: {text}")
# Add your business logic here
@app.route("/inbound-sms", methods=["POST"])
def inbound_sms():
originator = request.form.get("originator")
destination = request.form.get("destination")
date_time = request.form.get("date_time")
text = request.form.get("text")
# Process asynchronously to respond fast
thread = threading.Thread(
target=process_message,
args=(originator, destination, date_time, text)
)
thread.start()
return "+OK"
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000)
Node.js (Express)
const express = require("express");
const app = express();
app.use(express.urlencoded({ extended: true }));
function processMessage(originator, destination, dateTime, text) {
console.log(`From: ${originator}, To: ${destination}, Time: ${dateTime}`);
console.log(`Message: ${text}`);
// Add your business logic here
}
app.post("/inbound-sms", (req, res) => {
const { originator, destination, date_time, text } = req.body;
// Process asynchronously to respond fast
setImmediate(() => processMessage(originator, destination, date_time, text));
res.send("+OK");
});
app.listen(3000, () => console.log("Listening on port 3000"));
PHP
<?php
// inbound-sms.php
$originator = $_POST['originator'] ?? '';
$destination = $_POST['destination'] ?? '';
$date_time = $_POST['date_time'] ?? '';
$text = $_POST['text'] ?? '';
// Log the message
$logEntry = sprintf(
"[%s] From: %s, To: %s, Message: %s\n",
$date_time, $originator, $destination, $text
);
file_put_contents('inbound-sms.log', $logEntry, FILE_APPEND);
// Process your business logic here
// ...
// Required response
echo "+OK";
Java (Spring Boot)
import org.springframework.web.bind.annotation.*;
import java.util.concurrent.CompletableFuture;
@RestController
public class InboundSmsController {
@PostMapping("/inbound-sms")
public String handleInboundSms(
@RequestParam String originator,
@RequestParam String destination,
@RequestParam("date_time") String dateTime,
@RequestParam String text) {
// Process asynchronously
CompletableFuture.runAsync(() -> {
System.out.printf("From: %s, To: %s, Time: %s%n", originator, destination, dateTime);
System.out.printf("Message: %s%n", text);
// Add your business logic here
});
return "+OK";
}
}
Go (net/http)
package main
import (
"fmt"
"net/http"
)
func processMessage(originator, destination, dateTime, text string) {
fmt.Printf("From: %s, To: %s, Time: %s\n", originator, destination, dateTime)
fmt.Printf("Message: %s\n", text)
// Add your business logic here
}
func inboundSmsHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
r.ParseForm()
originator := r.FormValue("originator")
destination := r.FormValue("destination")
dateTime := r.FormValue("date_time")
text := r.FormValue("text")
// Process asynchronously
go processMessage(originator, destination, dateTime, text)
fmt.Fprint(w, "+OK")
}
func main() {
http.HandleFunc("/inbound-sms", inboundSmsHandler)
fmt.Println("Listening on :8080")
http.ListenAndServe(":8080", nil)
}
C# (ASP.NET)
using Microsoft.AspNetCore.Mvc;
[ApiController]
[Route("inbound-sms")]
public class InboundSmsController : ControllerBase
{
[HttpPost]
public IActionResult HandleInboundSms(
[FromForm] string originator,
[FromForm] string destination,
[FromForm(Name = "date_time")] string dateTime,
[FromForm] string text)
{
// Process asynchronously
_ = Task.Run(() =>
{
Console.WriteLine($"From: {originator}, To: {destination}, Time: {dateTime}");
Console.WriteLine($"Message: {text}");
// Add your business logic here
});
return Content("+OK");
}
}
Ruby (Sinatra)
require "sinatra"
def process_message(originator, destination, date_time, text)
puts "From: #{originator}, To: #{destination}, Time: #{date_time}"
puts "Message: #{text}"
# Add your business logic here
end
post "/inbound-sms" do
originator = params["originator"]
destination = params["destination"]
date_time = params["date_time"]
text = params["text"]
# Process asynchronously
Thread.new { process_message(originator, destination, date_time, text) }
"+OK"
end
Buone pratiche
Rispondi velocemente
Il tuo endpoint deve restituire +OK entro 1 secondo per evitare problemi di timeout. Delega tutta l'elaborazione a worker in background o thread asincroni.
:::tip Prestazioni
Rispondi sempre con +OK prima di elaborare il messaggio. Utilizza pattern asincroni (thread, code, job in background) per la logica di business.
:::
Gestisci i duplicati
A causa dei tentativi di reinvio, il tuo webhook potrebbe ricevere lo stesso messaggio più di una volta. Implementa l'idempotenza utilizzando un hash del messaggio:
import hashlib
seen_messages = set()
def is_duplicate(originator, destination, date_time, text):
msg_hash = hashlib.sha256(
f"{originator}{destination}{date_time}{text}".encode()
).hexdigest()
if msg_hash in seen_messages:
return True
seen_messages.add(msg_hash)
return False
:::caution Uso in produzione
L'esempio con set() in memoria sopra è solo a scopo illustrativo. In produzione, utilizza un archivio persistente come Redis o un database con scadenza basata su TTL.
:::
Registra tutto
Registra ogni messaggio in arrivo con timestamp, mittente e risultato dell'elaborazione. Questo è essenziale per il debug dei problemi di consegna e per l'audit.
Monitora i fallimenti
Configura gli avvisi per:
- Tempi di risposta del webhook superiori a 5 secondi
- Tasso di errore superiore all'1%
- Messaggi mancanti (lacune nelle sequenze attese)