Skip to main content

Webhooks

Webhooks let you receive real-time HTTP notifications when message events occur -- incoming messages, delivery confirmations, and failures.

Event types​

EventDescription
message.receivedAn inbound iMessage was received on your connected number
message.sentAn outbound message was successfully sent from your iPhone
message.deliveredA sent message was confirmed as delivered. This functionality is working only with Standard plan and higher.
message.failedA message failed to send

Create a webhook​

POST /v1/webhooks

Required permission: webhooks:manage

Request body​

FieldTypeRequiredDescription
urlstringYesHTTPS URL to receive webhook events
eventsstring[]YesArray of event types to subscribe to
phone_numberstringNoFilter events to a specific phone number

Example​

curl -X POST https://api.texting.blue/v1/webhooks \
-H "Content-Type: application/json" \
-H "x-api-key: tb_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-d '{
"url": "https://yourapp.com/webhook",
"events": ["message.received", "message.sent", "message.failed"]
}'

Response​

Status: 201 Created

{
"id": "wh_xxxxxxxxxxxx",
"url": "https://yourapp.com/webhook",
"events": ["message.received", "message.sent", "message.failed"],
"phone_number": null,
"secret": "whsec_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"active": true,
"created_at": "2026-02-07T12:00:00Z"
}
warning

Save the secret value immediately. It's only shown when the webhook is created and is required for signature verification.


Webhook payload​

When an event fires, Texting Blue sends an HTTP POST request to your webhook URL.

Headers​

HeaderDescription
Content-Typeapplication/json
x-textingblue-signatureHMAC-SHA256 signature of the request body
x-textingblue-eventThe event type (e.g., message.received)

Body​

{
"id": "evt_xxxxxxxxxxxx",
"type": "message.received",
"timestamp": "2026-02-07T12:00:00Z",
"data": {
"id": "msg_xxxxxxxxxxxx",
"from": "+14155551234",
"to": "+14155559876",
"content": "Hey, got your message!",
"media_url": null,
"received_at": "2026-02-07T12:00:00Z"
}
}

Signature verification​

Every webhook request includes an x-textingblue-signature header containing an HMAC-SHA256 signature. Always verify this signature to ensure the request came from Texting Blue.

The signature format is: sha256={hex_digest}

import crypto from 'crypto';

function verifyWebhookSignature(body, signature, secret) {
const expected = 'sha256=' + crypto
.createHmac('sha256', secret)
.update(body)
.digest('hex');

return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}

// In your Express handler:
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
const signature = req.headers['x-textingblue-signature'];
const isValid = verifyWebhookSignature(req.body, signature, WEBHOOK_SECRET);

if (!isValid) {
return res.status(401).send('Invalid signature');
}

const event = JSON.parse(req.body);
// Process the event...
res.json({ received: true });
});

List webhooks​

GET /v1/webhooks

Returns all webhooks for your team.

curl https://api.texting.blue/v1/webhooks \
-H "x-api-key: tb_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

Response​

{
"webhooks": [
{
"id": "wh_xxxxxxxxxxxx",
"url": "https://yourapp.com/webhook",
"events": ["message.received", "message.sent"],
"active": true,
"failure_count": 0,
"last_triggered_at": "2026-02-07T11:55:00Z",
"created_at": "2026-02-01T10:00:00Z"
}
]
}

Update a webhook​

PUT /v1/webhooks/:id

Update the URL, events, or active status of a webhook.

Request body​

FieldTypeRequiredDescription
urlstringNoNew webhook URL
eventsstring[]NoNew event types to subscribe to
activebooleanNoEnable or disable the webhook
curl -X PUT https://api.texting.blue/v1/webhooks/wh_xxxxxxxxxxxx \
-H "Content-Type: application/json" \
-H "x-api-key: tb_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-d '{"active": false}'

Delete a webhook​

DELETE /v1/webhooks/:id

Permanently remove a webhook.

curl -X DELETE https://api.texting.blue/v1/webhooks/wh_xxxxxxxxxxxx \
-H "x-api-key: tb_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

Response​

{
"deleted": true
}

Retry behavior​

If your webhook endpoint returns an error (HTTP status >= 400) or times out, Texting Blue retries the delivery:

  • Attempt 1: Immediate
  • Attempt 2: After 10 seconds
  • Attempt 3: After 60 seconds
  • Attempt 4: After 300 seconds (5 minutes)

After 10 consecutive failures across all deliveries, the webhook is automatically disabled. You'll need to re-enable it manually via the API or dashboard after fixing the issue.

Best practices​

  • Always verify webhook signatures before processing events
  • Return a 200 response quickly (within 5 seconds). Do heavy processing asynchronously.
  • Use a unique URL path per webhook for easier debugging
  • Monitor your webhook's failure_count in the dashboard
  • Handle duplicate events gracefully (use the id field for deduplication)

Next steps​