Webhooks
Learn how to configure and use webhooks to receive real-time notifications for SMS events in textbee.dev. Supports multiple webhook endpoints per account.
Webhooks let textbee.dev notify your server in real time when events happen, so you donβt need to poll the API.
What are Webhooks?
Webhooks are HTTP callbacks that textbee.dev sends to your server when specific events occur. This enables event-driven architectures and real-time integrations.
Supported Events
textbee.dev supports the following webhook events:
- MESSAGE_RECEIVED: When an SMS is received on your device
- MESSAGE_SENT: When an SMS has been sent from your device
- MESSAGE_DELIVERED: When an SMS has been delivered to the recipient
- MESSAGE_FAILED: When sending an SMS has failed
Setting Up Webhooks
Set up webhooks in the dashboard. You need a public URL that accepts HTTP POST requests and returns 200 to acknowledge receipt. You can register multiple webhook endpoints on the same account β for example one for production, one for a staging integration, and one for your CRM. Each endpoint has its own signing secret and event subscriptions.
- Open the dashboard and go to Webhooks (or Settings > Webhooks).
- Click Add Webhook or Create Webhook.
- (Optional) Give the webhook a short name so you can tell your endpoints apart.
- Enter your webhook URL (use HTTPS in production). Private/loopback hosts (e.g.
localhost,127.0.0.1, RFC1918 ranges) are rejected. - Select the events you want: MESSAGE_RECEIVED, MESSAGE_SENT, MESSAGE_DELIVERED, MESSAGE_FAILED (or a subset).
- (Recommended) Set a signing secret (at least 20 characters) for signature verification.
- Save. textbee.dev will send POST requests to your URL when the selected events occur. If multiple webhooks subscribe to the same event, each one receives its own independent delivery.
Webhook Payloads
Payloads are JSON with event, timestamp, and data. Return 200 quickly and process in the background if needed.
MESSAGE_RECEIVED
{
"event": "MESSAGE_RECEIVED",
"timestamp": "2026-01-20T10:30:00Z",
"data": {
"_id": "abc123",
"sender": "+1234567890",
"message": "Hello!",
"receivedAt": "2025-01-15T10:30:00Z",
"device": { "_id": "device123", "enabled": true, "model": "Pixel 7" },
"createdAt": "2025-01-15T10:30:01Z",
"updatedAt": "2025-01-15T10:30:01Z"
}
}
MESSAGE_SENT
Sent when an SMS has been sent from your device. data includes message id, recipient(s), and device info.
MESSAGE_DELIVERED
Sent when the carrier reports delivery. data includes message id, recipient, and delivery time.
MESSAGE_FAILED
Sent when sending failed. data includes message id, recipient, and error details.
Webhook Security
Signature Verification
To verify that webhooks are coming from textbee.dev, use signature verification:
- When creating a webhook, set a secret (a random string)
- textbee.dev will include a signature in the
X-Signatureheader - Verify the signature on your server using the secret configured for that specific webhook
If you have multiple webhooks, each one has its own signing secret β use the secret that matches the receiving endpoint.
Example: Signature Verification
const crypto = require('crypto');
function verifyWebhookSignature(payload, signature, secret) {
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(JSON.stringify(payload))
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expectedSignature)
);
}
app.post('/webhook/textbee', express.raw({ type: 'application/json' }), (req, res) => {
const signature = req.headers['x-signature'];
const secret = process.env.WEBHOOK_SECRET;
if (!verifyWebhookSignature(req.body, signature, secret)) {
return res.status(401).json({ error: 'Invalid signature' });
}
const payload = JSON.parse(req.body);
// Process webhook...
res.status(200).json({ success: true });
});
Webhook Handler Examples
Node.js/Express
const express = require('express');
const app = express();
app.use(express.json());
app.post('/webhook/textbee', (req, res) => {
const { event, data } = req.body;
switch (event) {
case 'MESSAGE_RECEIVED':
console.log(`Received from ${data.sender}: ${data.message}`);
break;
case 'MESSAGE_SENT':
console.log(`Sent to ${data.recipient}`);
break;
case 'MESSAGE_DELIVERED':
console.log(`Delivered to ${data.recipient}`);
break;
case 'MESSAGE_FAILED':
console.error(`Failed to ${data.recipient}: ${data.error}`);
break;
default:
console.log(`Event: ${event}`);
}
res.status(200).json({ success: true });
});
app.listen(3000, () => {
console.log('Webhook server listening on port 3000');
});
Python/Flask
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route('/webhook/textbee', methods=['POST'])
def webhook():
payload = request.json
event = payload.get('event')
data = payload.get('data') or {}
if event == 'MESSAGE_RECEIVED':
print(f"Received from {data.get('sender')}: {data.get('message')}")
elif event == 'MESSAGE_SENT':
print(f"Sent to {data.get('recipient')}")
elif event == 'MESSAGE_DELIVERED':
print(f"Delivered to {data.get('recipient')}")
elif event == 'MESSAGE_FAILED':
print(f"Failed to {data.get('recipient')}: {data.get('error')}")
return jsonify({'success': True}), 200
if __name__ == '__main__':
app.run(port=3000)
Webhook Retries
If your webhook endpoint returns a non-2xx status code, the delivery attempt is considered failed.
Best Practices
- Always return 200 OK immediately after receiving a webhook (process asynchronously if needed)
- Implement idempotency - handle duplicate webhooks gracefully
- Verify webhook signatures to ensure authenticity
- Use HTTPS for webhook URLs in production
- Log all webhook events for debugging and auditing
- Handle errors gracefully - don't let webhook processing crash your server
- Process webhooks asynchronously if processing takes time
- Monitor webhook delivery - set up alerts for failed webhooks
Next Steps
- Receiving SMS - Process incoming messages with webhooks
- Sending SMS - Reply to received messages
- Overview - Explore capabilities
Need help? Check our FAQ or contact support.