Webhooks
Webhooks allow you to receive real-time notifications when events occur in your SendBlue account. You can configure multiple webhooks for different event types and manage them via our API.
In this documentation, we will cover how to:
- Understand webhook types and formats
- Set up and manage webhooks
- Secure your webhook endpoints
- Handle webhook events
Webhook Types
Section titled “Webhook Types”The following webhook types are supported:
| Type | Description |
|---|---|
| receive | Triggered when you receive an inbound message |
| outbound | Triggered when an outbound message is sent |
| call_log | Triggered when a call log is received |
| line_blocked | Triggered when a line is blocked |
| line_assigned | Triggered when a line is assigned |
| contact_created | Triggered when a contact is created |
Webhook Format
Section titled “Webhook Format”Webhooks can be specified in two formats:
- Simple URL string:
"https://example.com/webhook" - Object with URL and secret:
{ "url": "https://example.com/webhook", "secret": "my-secret" }
Managing Webhooks
Section titled “Managing Webhooks”You can manage your webhooks using the following API endpoints:
- List all webhooks - Get all configured webhooks
- Create webhooks - Add new webhooks (appends to existing)
- Update webhooks - Replace all webhooks
- Delete webhooks - Remove specific webhooks
Listing Webhooks
Section titled “Listing Webhooks”Retrieve all webhooks configured for your account:
curl -X GET https://api.sendblue.co/api/v2/account/webhooks \ -H "sb-api-key-id: YOUR_API_KEY" \ -H "sb-api-secret-key: YOUR_API_SECRET"Response:
{ "status": "OK", "webhooks": { "receive": [ "https://example.com/webhook1", { "url": "https://example.com/webhook2", "secret": "webhook-secret" } ], "call_log": [], "line_blocked": [], "line_assigned": [], "outbound": [], "contact_created": ["https://example.com/contact-webhook"], "globalSecret": "global-secret" }}Adding Webhooks
Section titled “Adding Webhooks”Add new webhooks to your account. This endpoint appends to the existing webhook list:
curl -X POST https://api.sendblue.co/api/v2/account/webhooks \ -H "sb-api-key-id: YOUR_API_KEY" \ -H "sb-api-secret-key: YOUR_API_SECRET" \ -H "Content-Type: application/json" \ -d '{ "webhooks": [ "https://example.com/new-webhook", { "url": "https://example.com/webhook-with-secret", "secret": "my-webhook-secret" } ], "type": "receive" }'Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
| webhooks | array | Yes | Array of webhook URLs or webhook objects |
| type | string | No | Webhook type (default: receive) |
| globalSecret | string | No | Global secret to apply to all webhooks |
Replacing All Webhooks
Section titled “Replacing All Webhooks”Replace the entire webhook configuration for your account:
curl -X PUT https://api.sendblue.co/api/v2/account/webhooks \ -H "sb-api-key-id: YOUR_API_KEY" \ -H "sb-api-secret-key: YOUR_API_SECRET" \ -H "Content-Type: application/json" \ -d '{ "webhooks": { "receive": ["https://example.com/webhook"], "call_log": ["https://example.com/call-webhook"], "contact_created": ["https://example.com/contact-webhook"], "globalSecret": "my-global-secret" } }'Deleting Webhooks
Section titled “Deleting Webhooks”Remove specific webhooks from your account:
curl -X DELETE https://api.sendblue.co/api/v2/account/webhooks \ -H "sb-api-key-id: YOUR_API_KEY" \ -H "sb-api-secret-key: YOUR_API_SECRET" \ -H "Content-Type: application/json" \ -d '{ "webhooks": ["https://example.com/webhook-to-delete"], "type": "receive" }'Webhook Security
Section titled “Webhook Security”SendBlue supports multiple ways to secure your webhook endpoints:
- Per-webhook secret: Set a
secreton individual webhook objects - Global secret: Set a
globalSecretthat applies to all webhooks - Legacy secret: The
secretfield at the root level (older format)
When you configure a secret, SendBlue will include it in the webhook request headers, allowing you to verify that the request is genuinely from SendBlue.
// Example: Setting up a webhook with a secretconst response = await fetch('https://api.sendblue.co/api/v2/account/webhooks', { method: 'POST', headers: { 'sb-api-key-id': 'YOUR_API_KEY', 'sb-api-secret-key': 'YOUR_API_SECRET', 'Content-Type': 'application/json' }, body: JSON.stringify({ webhooks: [ { url: 'https://myapp.com/webhooks/sendblue', secret: 'my-secure-secret-123' } ], type: 'receive' })});Handling Webhooks
Section titled “Handling Webhooks”Inbound Message Webhook
Section titled “Inbound Message Webhook”When you receive an inbound message, SendBlue will POST to your configured receive webhook with the following payload:
{ "content": "Hello!", "media_url": "https://example.com/image.jpg", "is_outbound": false, "status": "RECEIVED", "message_handle": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx", "date_sent": "2020-09-10T06:15:05.962Z", "date_updated": "2020-09-10T06:15:14.115Z", "from_number": "+19998887777", "number": "+19998887777", "to_number": "+15122164639", "was_downgraded": false}Outbound Message Webhook
Section titled “Outbound Message Webhook”For outbound messages, you can use the status_callback parameter when sending a message, or configure an outbound webhook to receive all outbound message status updates:
{ "content": "Hello world!", "is_outbound": true, "status": "DELIVERED", "error_code": null, "error_message": null, "message_handle": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx", "date_sent": "2020-09-10T06:15:05.962Z", "date_updated": "2020-09-10T06:15:14.115Z", "from_number": "+15122164639", "number": "+19998887777", "to_number": "+19998887777", "was_downgraded": false, "plan": "blue"}Webhook Payload Fields
Section titled “Webhook Payload Fields”| Field | Type | Description |
|---|---|---|
| accountEmail | string | Associated account email |
| content | string | Message content |
| is_outbound | boolean | True if message is sent, false if message is received |
| media_url | string | A CDN link to any media attached to the message |
| status | string | The current status of the message |
| error_code | int | Error code (null if no error) |
| error_message | string | Descriptive error message (null if no error) |
| message_handle | string | Sendblue message handle |
| date_sent | string | ISO 8601 formatted date string of when message was created |
| date_updated | string | ISO 8601 formatted date string of when message was last updated |
| from_number | string | E.164 formatted phone number of the message dispatcher |
| number | string | E.164 formatted phone number of your end-user |
| to_number | string | E.164 formatted phone number of the message recipient |
| was_downgraded | boolean | True if the end user does not support iMessage |
| plan | string | Value of the Sendblue account plan |
Best Practices
Section titled “Best Practices”1. Use HTTPS Only
Section titled “1. Use HTTPS Only”All webhook URLs must use HTTPS to ensure secure communication.
2. Implement Idempotency
Section titled “2. Implement Idempotency”Your webhook endpoints should be idempotent, as they may receive duplicate events. Use the message_handle field to deduplicate events.
// Example: Idempotent webhook handlerconst processedMessages = new Set();
app.post('/webhook', (req, res) => { const { message_handle } = req.body;
if (processedMessages.has(message_handle)) { return res.status(200).send('Already processed'); }
processedMessages.add(message_handle); // Process the webhook... res.status(200).send('OK');});3. Verify Webhook Signatures
Section titled “3. Verify Webhook Signatures”Always verify that webhook requests are coming from SendBlue by checking the secret in the request headers.
4. Return Appropriate Status Codes
Section titled “4. Return Appropriate Status Codes”- Return 200-299 for successful processing
- Return 410 Gone if you want SendBlue to automatically remove the webhook
5. Handle Errors Gracefully
Section titled “5. Handle Errors Gracefully”Implement proper error handling and logging in your webhook endpoints to troubleshoot issues.
Error Responses
Section titled “Error Responses”401 Unauthorized
Section titled “401 Unauthorized”{ "status": "ERROR", "message": "Unauthorized"}Authentication failed. Check your API credentials.
400 Bad Request
Section titled “400 Bad Request”{ "status": "ERROR", "message": "Missing or invalid webhooks array"}The request body is malformed or missing required fields.
500 Internal Server Error
Section titled “500 Internal Server Error”{ "status": "ERROR", "message": "Error message details"}An internal error occurred. Contact support if this persists.
Examples
Section titled “Examples”Example 1: Basic Webhook Setup
Section titled “Example 1: Basic Webhook Setup”// Add a simple webhook for receiving messagesconst response = await fetch('https://api.sendblue.co/api/v2/account/webhooks', { method: 'POST', headers: { 'sb-api-key-id': 'YOUR_API_KEY', 'sb-api-secret-key': 'YOUR_API_SECRET', 'Content-Type': 'application/json' }, body: JSON.stringify({ webhooks: ['https://myapp.com/webhooks/sendblue'], type: 'receive' })});
const data = await response.json();console.log(data);Example 2: Multi-Type Webhook Configuration
Section titled “Example 2: Multi-Type Webhook Configuration”// Configure webhooks for multiple event typesconst response = await fetch('https://api.sendblue.co/api/v2/account/webhooks', { method: 'PUT', headers: { 'sb-api-key-id': 'YOUR_API_KEY', 'sb-api-secret-key': 'YOUR_API_SECRET', 'Content-Type': 'application/json' }, body: JSON.stringify({ webhooks: { receive: ['https://myapp.com/webhooks/receive'], outbound: ['https://myapp.com/webhooks/outbound'], call_log: ['https://myapp.com/webhooks/calls'], contact_created: ['https://myapp.com/webhooks/contacts'], globalSecret: 'my-global-secret' } })});Example 3: Express.js Webhook Handler
Section titled “Example 3: Express.js Webhook Handler”const express = require('express');const app = express();
app.use(express.json());
app.post('/webhooks/sendblue', (req, res) => { const { content, from_number, message_handle, is_outbound, status } = req.body;
console.log(`New message from ${from_number}: ${content}`); console.log(`Message handle: ${message_handle}`); console.log(`Status: ${status}`);
// Process the message here // ...
// Always respond with 200 to acknowledge receipt res.status(200).send('OK');});
app.listen(3000, () => { console.log('Webhook server running on port 3000');});Additional Notes
Section titled “Additional Notes”- Webhook URLs are automatically validated to ensure they are valid HTTPS URLs
- The API maintains backward compatibility with legacy webhook formats
- The
receivewebhook type is the most commonly used for inbound messages - You can configure multiple webhooks for the same event type for redundancy