Incoming SMS Webhooks
Receive real-time notifications when SMS messages are received on your channels.
Overview
When an SMS is received on your channel, the platform will call your configured webhook endpoint, allowing you to process the message and optionally return an auto-reply.
✅ New: HTTP POST (JSON) method now available! Infobip-compatible format with
HMAC-SHA256 signatures.
Webhook Methods
Choose the method that best fits your integration:
| Method | Format | Security | Best For |
|---|---|---|---|
| POST (JSON) | JSON payload | HMAC-SHA256 | Modern APIs, Infobip migration |
| GET | Query parameters | Security key + Base64 | Simple integrations, legacy systems |
| SOAP | SOAP 1.2 XML | Security key in body | Enterprise systems, .NET SOAP |
HTTP POST (JSON) Method
Recommended for new integrations. Modern JSON format with HMAC-SHA256 signatures.
POST
https://your-api.com/webhooks/incoming-sms
Request Headers
Content-Type: application/json
X-Signature: sha256=<hmac-sha256-signature>
User-Agent: SMSDesk-Webhook/1.0
Request Payload
{
"type": "incoming_sms",
"timestamp": "2026-07-11T07:22:00.0000000Z",
"data": {
"messageId": "123456789",
"from": "+264812182326",
"to": "44880",
"text": "Hello, this is a test message",
"receivedAt": "2026-07-11T07:22:00.0000000Z",
"clientId": 1059
}
}
Payload Fields
| Field | Type | Description |
|---|---|---|
type |
string | Always "incoming_sms" |
timestamp |
string (ISO 8601) | UTC timestamp when webhook was sent |
data.messageId |
string | Internal message ID |
data.from |
string | Sender mobile number (international format) |
data.to |
string | Recipient channel/shortcode |
data.text |
string | Message content |
data.receivedAt |
string (ISO 8601) | UTC timestamp when message was received |
data.clientId |
integer | Your client ID |
Response (Auto-Reply)
Your endpoint can return a plain text response that will be sent as an auto-reply SMS:
HTTP/1.1 200 OK
Content-Type: text/plain
Thank you for your message. We will respond shortly.
Return empty body to use the default auto-response configured in the portal:
HTTP/1.1 200 OK
Content-Type: text/plain
HTTP GET Method
Legacy method using query parameters.
GET
https://your-api.com/webhook/incoming?securitykey=...&sender=...&message=...
Query Parameters
| Parameter | Type | Description |
|---|---|---|
securitykey |
string | Your configured security key |
incomingsmsid |
long | Unique message ID |
timestamp |
long | Unix timestamp (seconds) |
sender |
string | Sender mobile number |
recipient |
string | Your channel/shortcode |
msg |
string | Base64-encoded message wrapped with security key |
message |
string | Plain text message content |
Security
The msg parameter contains the message wrapped with your security key and Base64-encoded:
- Original message:
Hello - Wrapped:
<YOUR_KEY>Hello<YOUR_KEY> - Base64 encoded:
PFlPVVJfS0VZPkhlbGxvPFlPVVJfS0VZPg==
SOAP Method
SOAP 1.2 XML envelope for enterprise integrations.
POST
https://your-api.com/soap/incoming
SOAP Request
<soap12:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soap12="http://www.w3.org/2003/05/soap-envelope">
<soap12:Body>
<incomingsms xmlns="http://tempuri.org/">
<incomingsmsid>12345</incomingsmsid>
<timestamp>1720522800</timestamp>
<sender>+264811234567</sender>
<msg>PFlPVVJfS0VZPkhlbGxvPFlPVVJfS0VZPg==</msg>
<securitykey>YOUR_KEY</securitykey>
</incomingsms>
</soap12:Body>
</soap12:Envelope>
Configuration
Web Portal Setup
- Log in to your SMSDESK portal (e.g.,
https://desk.sms.com.nafor Namibia deployment) - Navigate to Account → SMS Channels
- Click on your channel to edit
- In Webhooks section, configure:
- Incoming SMS Handler: Select POST (JSON), GET, or SOAP
- WebService URL: Your webhook endpoint
- Security Key: Your secret (use "Generate" button for POST)
- Save changes
Code Examples
Python / Flask
from flask import Flask, request, jsonify
import hmac
import hashlib
app = Flask(__name__)
SECRET = 'your-secret-here'
@app.route('/webhooks/incoming-sms', methods=['POST'])
def incoming_sms():
# Get raw body
payload = request.get_data(as_text=True)
# Verify signature
signature = request.headers.get('X-Signature', '')
if not verify_signature(payload, SECRET, signature):
return jsonify({'error': 'Invalid signature'}), 401
# Parse JSON
webhook = request.get_json()
process_message(webhook['data'])
# Return auto-reply (optional)
return 'Thank you for your message!', 200
def verify_signature(payload, secret, sig):
if not sig or not sig.startswith('sha256='):
return False
expected = hmac.new(
secret.encode('utf-8'),
payload.encode('utf-8'),
hashlib.sha256
).hexdigest()
received = sig[7:] # Remove 'sha256=' prefix
return hmac.compare_digest(expected, received)
def process_message(data):
print(f"Received SMS from {data['from']}: {data['text']}")
# Add your processing logic here
if __name__ == '__main__':
app.run(port=3000)
JavaScript / Node.js / Express
const express = require('express');
const crypto = require('crypto');
const app = express();
const SECRET = 'your-secret-here';
app.post('/webhooks/incoming-sms',
express.text({ type: 'application/json' }),
(req, res) => {
// Verify signature
const signature = req.headers['x-signature'];
if (!verifySignature(req.body, SECRET, signature)) {
return res.status(401).json({ error: 'Invalid signature' });
}
// Parse and process
const webhook = JSON.parse(req.body);
processMessage(webhook.data);
// Return auto-reply (optional)
res.send('Thank you for your message!');
}
);
function verifySignature(payload, secret, sig) {
if (!sig || !sig.startsWith('sha256=')) return false;
const hmac = crypto.createHmac('sha256', secret);
hmac.update(payload, 'utf8');
const expected = hmac.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(sig.substring(7)),
Buffer.from(expected)
);
}
function processMessage(data) {
console.log(`Received SMS from ${data.from}: ${data.text}`);
// Add your processing logic here
}
app.listen(3000);
C# / ASP.NET Core
using Microsoft.AspNetCore.Mvc;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
[ApiController]
[Route("webhooks/incoming-sms")]
public class IncomingSmsController : ControllerBase
{
private readonly string _secret = "your-secret-here";
[HttpPost]
public async Task<IActionResult> Receive()
{
using var reader = new StreamReader(Request.Body);
string payload = await reader.ReadToEndAsync();
// Verify signature
string signature = Request.Headers["X-Signature"];
if (!VerifySignature(payload, _secret, signature))
return Unauthorized(new { error = "Invalid signature" });
// Parse and process
var webhook = JsonSerializer.Deserialize<IncomingSmsWebhook>(payload);
ProcessMessage(webhook.Data);
// Return auto-reply (optional)
return Ok("Thank you for your message!");
}
private bool VerifySignature(string payload, string secret, string sig)
{
if (string.IsNullOrEmpty(sig) || !sig.StartsWith("sha256="))
return false;
using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secret));
var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(payload));
var expected = BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant();
return sig.Substring(7) == expected;
}
private void ProcessMessage(IncomingSmsData data)
{
Console.WriteLine($"Received SMS from {data.From}: {data.Text}");
// Add your processing logic here
}
}
PHP
<?php
$secret = 'your-secret-here';
// Get raw POST body
$payload = file_get_contents('php://input');
// Get signature header
$signature = $_SERVER['HTTP_X_SIGNATURE'] ?? '';
// Verify signature
if (!verifySignature($payload, $secret, $signature)) {
http_response_code(401);
echo json_encode(['error' => 'Invalid signature']);
exit;
}
// Parse JSON
$webhook = json_decode($payload, true);
processMessage($webhook['data']);
// Return auto-reply (optional)
http_response_code(200);
echo 'Thank you for your message!';
function verifySignature($payload, $secret, $sig) {
if (empty($sig) || strpos($sig, 'sha256=') !== 0) {
return false;
}
$expected = hash_hmac('sha256', $payload, $secret);
$received = substr($sig, 7); // Remove 'sha256=' prefix
return hash_equals($expected, $received);
}
function processMessage($data) {
error_log("Received SMS from {$data['from']}: {$data['text']}");
// Add your processing logic here
}
?>
Java / Spring Boot
import org.springframework.web.bind.annotation.*;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
@RestController
@RequestMapping("/webhooks/incoming-sms")
public class IncomingSmsController {
private static final String SECRET = "your-secret-here";
@PostMapping
public ResponseEntity<String> receiveIncomingSms(
@RequestBody String payload,
@RequestHeader("X-Signature") String signature) {
// Verify signature
if (!verifySignature(payload, SECRET, signature)) {
return ResponseEntity.status(401)
.body("{\"error\": \"Invalid signature\"}");
}
// Parse JSON
ObjectMapper mapper = new ObjectMapper();
IncomingSmsWebhook webhook = mapper.readValue(payload, IncomingSmsWebhook.class);
processMessage(webhook.getData());
// Return auto-reply (optional)
return ResponseEntity.ok("Thank you for your message!");
}
private boolean verifySignature(String payload, String secret, String sig) {
if (sig == null || !sig.startsWith("sha256=")) {
return false;
}
try {
Mac hmac = Mac.getInstance("HmacSHA256");
SecretKeySpec secretKey = new SecretKeySpec(
secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
hmac.init(secretKey);
byte[] hash = hmac.doFinal(payload.getBytes(StandardCharsets.UTF_8));
String expected = bytesToHex(hash);
String received = sig.substring(7);
return MessageDigest.isEqual(
expected.getBytes(StandardCharsets.UTF_8),
received.getBytes(StandardCharsets.UTF_8)
);
} catch (Exception e) {
return false;
}
}
private String bytesToHex(byte[] bytes) {
StringBuilder result = new StringBuilder();
for (byte b : bytes) {
result.append(String.format("%02x", b));
}
return result.toString();
}
private void processMessage(IncomingSmsData data) {
System.out.println("Received SMS from " + data.getFrom() + ": " + data.getText());
// Add your processing logic here
}
}
Ready to Start? Configure your webhook in your
SMSDESK portal and start receiving incoming SMS messages!
