Twilio Status Callbacks to an Internal Service Using Skooh
Forward Twilio SMS and voice status callbacks to private handlers. Handle retries and duplicates gracefully.
Overview
Twilio sends status callbacks for SMS delivery, voice call events, and more. These callbacks let you track message delivery, handle call recordings, and respond to errors.
With Skooh, your internal callback handlers receive these events without being exposed to the internet.
Setting Up
1. Create Skooh Endpoint
Create an endpoint in Skooh and copy your webhook URL.
2. Run the Skooh Proxy
Using the Desktop App:
- Download the Skooh Desktop App
- Enter your API key and hook token
- Set forwarding URL (e.g., `http://localhost:3000/callbacks/twilio\`)
- Click Connect
Using Docker:
docker run -d \\
--name skooh-twilio \\
-e SKOOH_TOKEN=your_token \\
-e SKOOH_API_KEY=your_api_key \\
-e FORWARD_URL=http://messaging-service:3000/callbacks/twilio \\
--restart unless-stopped \\
skooh/proxy
3. Configure Twilio
Note: See Twilio's webhook documentation for current configuration options.
When sending SMS, include your Skooh URL as the status callback:
const twilio = require('twilio');
const client = twilio(accountSid, authToken);
await client.messages.create({
body: 'Hello from Twilio!',
from: '+1234567890',
to: '+0987654321',
statusCallback: 'https://api.skooh.io/hook/your_token'
});
Or configure it in your TwiML apps and phone numbers.
Handling Status Callbacks
Twilio sends form-encoded data (not JSON):
const express = require('express');
const app = express();
// Parse form data
app.use(express.urlencoded({ extended: false }));
app.post('/callbacks/twilio', (req, res) => {
const {
MessageSid,
MessageStatus,
To,
From,
ErrorCode,
ErrorMessage
} = req.body;
console.log(\`Message \${MessageSid}: \${MessageStatus}\`);
switch (MessageStatus) {
case 'queued':
// Message accepted by Twilio
break;
case 'sent':
// Message sent to carrier
break;
case 'delivered':
// Message delivered to device
await markMessageDelivered(MessageSid);
break;
case 'failed':
case 'undelivered':
await handleDeliveryFailure(MessageSid, ErrorCode, ErrorMessage);
break;
}
// Twilio expects empty 200 response
res.status(200).send();
});
Validating Twilio Requests
Twilio signs requests. Always verify in production:
const twilio = require('twilio');
function validateTwilioRequest(req) {
const authToken = process.env.TWILIO_AUTH_TOKEN;
const twilioSignature = req.headers['x-twilio-signature'];
const url = 'https://api.skooh.io/hook/your_token'; // Your Skooh URL
return twilio.validateRequest(
authToken,
twilioSignature,
url,
req.body
);
}
app.post('/callbacks/twilio', (req, res) => {
if (!validateTwilioRequest(req)) {
return res.status(403).send('Invalid signature');
}
// Process callback...
});
Handling Retries and Duplicates
Twilio retries failed callbacks. Make your handlers idempotent:
const processedCallbacks = new Map(); // Use Redis in production
app.post('/callbacks/twilio', async (req, res) => {
const { MessageSid, MessageStatus } = req.body;
const callbackKey = \`\${MessageSid}:\${MessageStatus}\`;
// Check for duplicate
if (processedCallbacks.has(callbackKey)) {
console.log(\`Duplicate callback for \${callbackKey}, skipping\`);
return res.status(200).send();
}
// Process the callback
await processStatusUpdate(req.body);
processedCallbacks.set(callbackKey, Date.now());
res.status(200).send();
});
Voice Call Status Callbacks
Voice calls also trigger status callbacks:
app.post('/callbacks/twilio', (req, res) => {
const { CallSid, CallStatus, Duration, RecordingUrl } = req.body;
switch (CallStatus) {
case 'ringing':
await logCallStarted(CallSid);
break;
case 'in-progress':
await updateCallStatus(CallSid, 'connected');
break;
case 'completed':
await handleCallComplete(CallSid, Duration, RecordingUrl);
break;
case 'failed':
case 'no-answer':
case 'busy':
await handleCallFailure(CallSid, CallStatus);
break;
}
res.status(200).send();
});
Using Replay for Testing
- Send a test SMS
- Wait for status callbacks in Skooh dashboard
- Click Replay to test your handler
- Test different status scenarios without sending more messages
Conclusion
Skooh bridges Twilio's callbacks to your internal services. Combined with proper validation and idempotency, you get reliable SMS/voice tracking behind your firewall.
Written by Skooh Team