The Inbound SMS Webhook allows you to receive SMS messages sent to your shortcodes in real-time. When a user sends an SMS to your shortcode, the Tech231 Platform will immediately POST the message details to your configured webhook endpoint.
This guide provides the complete specification for implementing and integrating inbound SMS webhooks into your application.
Prerequisites
Before setting up inbound SMS webhooks, you need:
An active Tech231 Platform account
An approved shortcode (numeric or shared with keywords)
A publicly accessible HTTPS endpoint to receive webhooks
Valid SSL certificate on your webhook endpoint
Webhook Configuration
Setting Up Your Webhook
Navigate to your Tech231 Platform dashboard
Go to Settings → Webhooks
Click "Add Webhook Endpoint"
Enter your webhook details:
URL: Your HTTPS endpoint (e.g., https://api.yourcompany.com/webhooks/sms)
Secret: A secure random string for signature verification
Events: Select sms.received
Active: Enable the webhook
Click "Save" and "Test Webhook" to verify connectivity
Webhook URL Requirements
Your webhook endpoint MUST:
Use HTTPS (HTTP is not supported)
Have a valid SSL/TLS certificate
Respond within 5 seconds
Return HTTP status codes 200-299 for success
Be publicly accessible (no localhost or private IPs)
Webhook Payload
Request Format
When an SMS is received, we'll send a POST request to your webhook URL:
{ "eventType": "sms.received", "eventId": "evt_multipart_456", "timestamp": "2024-01-15T14:25:30Z", "data": { "messageId": "msg_inbound_002", "from": "+447911123456", "to": "67890", "message": "FEEDBACK This is a very long message that exceeds the standard SMS length limit and therefore gets split into multiple parts. The platform automatically reassembles these parts into a single message for your convenience.", "receivedAt": "2024-01-15T14:25:30Z", "keyword": "FEEDBACK", "shortcode": "67890", "carrier": "Vodafone", "country": "GB", "encoding": "GSM-7", "parts": 2, "metadata": { "tenantId": "tenant_abc123", "organizationId": "org_xyz789" } }}
Every webhook request includes an X-Tech231-Signature header containing an HMAC-SHA256 signature. You MUST verify this signature to ensure the request is authentic.
import hmacimport hashlibdef verify_webhook_signature(payload: bytes, signature: str, secret: str) -> bool: # Remove 'sha256=' prefix if present signature_hash = signature[7:] if signature.startswith('sha256=') else signature # Compute expected signature expected_signature = hmac.new( secret.encode('utf8'), payload, hashlib.sha256 ).hexdigest() # Use timing-safe comparison return hmac.compare_digest(signature_hash, expected_signature)# Usage in Flaskfrom flask import Flask, request, jsonifyapp = Flask(__name__)@app.route('/webhooks/sms', methods=['POST'])def handle_webhook(): signature = request.headers.get('X-Tech231-Signature') payload = request.get_data() if not verify_webhook_signature(payload, signature, WEBHOOK_SECRET): return 'Invalid signature', 401 event = request.get_json() # Process the event... return jsonify({'received': True}), 200
C# (.NET):
Code
using System.Security.Cryptography;using System.Text;public class WebhookVerifier{ public static bool VerifySignature(string payload, string signature, string secret) { // Remove 'sha256=' prefix if present var signatureHash = signature.StartsWith("sha256=") ? signature.Substring(7) : signature; // Compute expected signature using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secret)); var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(payload)); var expectedSignature = BitConverter.ToString(hash) .Replace("-", "").ToLowerInvariant(); // Use timing-safe comparison return CryptographicOperations.FixedTimeEquals( Encoding.UTF8.GetBytes(signatureHash), Encoding.UTF8.GetBytes(expectedSignature) ); }}// Usage in ASP.NET Core[HttpPost("webhooks/sms")]public async Task<IActionResult> HandleWebhook(){ using var reader = new StreamReader(Request.Body); var payload = await reader.ReadToEndAsync(); var signature = Request.Headers["X-Tech231-Signature"].ToString(); if (!WebhookVerifier.VerifySignature(payload, signature, _webhookSecret)) { return Unauthorized("Invalid signature"); } var webhookEvent = JsonSerializer.Deserialize<WebhookEvent>(payload); // Process the event... return Ok(new { received = true });}
Additional Security Headers
Header
Description
X-Tech231-Event-Id
Unique event identifier for idempotency checking
X-Tech231-Timestamp
Request timestamp for replay attack prevention
User-Agent
Always Tech231-Webhook/1.0
Timestamp Verification:
Reject requests with timestamps older than 5 minutes to prevent replay attacks:
Code
function isTimestampValid(timestamp) { const eventTime = new Date(timestamp); const now = new Date(); const fiveMinutes = 5 * 60 * 1000; return (now - eventTime) < fiveMinutes;}