--- title: Webhooks | Sendblue Docs description: Real-time event notifications | iMessage For Business --- 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: 1. [Understand webhook types and formats](#webhook-types) 2. [Set up and manage webhooks](#managing-webhooks) 3. [Secure your webhook endpoints](#webhook-security) 4. [Handle webhook events](#handling-webhooks) ## 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 | | **typing\_indicator** | Triggered when a contact starts or stops typing | | **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 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" }` For `contact_created` type, only URL strings are supported. ## Managing Webhooks You can manage your webhooks using the following API endpoints: - [List all webhooks](/api/resources/webhooks/methods/list/index.md) - Get all configured webhooks - [Create webhooks](/api/resources/webhooks/methods/create/index.md) - Add new webhooks (appends to existing) - [Update webhooks](/api/resources/webhooks/methods/update/index.md) - Replace all webhooks - [Delete webhooks](/api/resources/webhooks/methods/delete/index.md) - Remove specific webhooks ### Listing Webhooks Retrieve all webhooks configured for your account: Terminal window ``` curl -X GET https://api.sendblue.co/api/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 Add new webhooks to your account. This endpoint appends to the existing webhook list: Terminal window ``` curl -X POST https://api.sendblue.co/api/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 Replace the entire webhook configuration for your account: Terminal window ``` curl -X PUT https://api.sendblue.co/api/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" } }' ``` This endpoint completely replaces your webhook configuration. Any webhooks not included in the request will be removed. ### Deleting Webhooks Remove specific webhooks from your account: Terminal window ``` curl -X DELETE https://api.sendblue.co/api/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 Sendblue supports multiple ways to secure your webhook endpoints: - **Per-webhook secret**: Set a `secret` on individual webhook objects - **Global secret**: Set a `globalSecret` that applies to all webhooks - **Legacy secret**: The `secret` field 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 secret const response = await fetch("https://api.sendblue.co/api/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 ### Inbound Message Webhook When you receive an inbound message, Sendblue will POST to your configured `receive` webhook with the following payload: ``` { "accountEmail": "your-account-email", "content": "Hello!", "is_outbound": false, "status": "RECEIVED", "error_code": null, "error_message": null, "error_reason": null, "message_handle": "99DCC379-DD76-4712-BA65-11EFB33B8CD6", "date_sent": "2025-12-12T15:41:20.932Z", "date_updated": "2025-12-12T15:41:20.995Z", "from_number": "+19998887777", "number": "+19998887777", "to_number": "+15122164639", "was_downgraded": null, "plan": "dedicated", "media_url": "", "message_type": "message", "group_id": "", "participants": ["+19998887777", "+15122164639"], "send_style": "", "opted_out": false, "error_detail": null, "sendblue_number": "+15122164639", "service": "iMessage", "group_display_name": null } ``` ### 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: ``` { "accountEmail": "your-account-email", "content": "Hello world!", "is_outbound": true, "status": "SENT", "error_code": null, "error_message": null, "error_reason": null, "message_handle": "5a17319e-cbcf-443e-897e-d8b0c04b1b09", "date_sent": "2025-12-12T15:35:35.410Z", "date_updated": "2025-12-12T15:35:35.410Z", "from_number": "+18649820355", "number": "+19998887777", "to_number": "+19998887777", "was_downgraded": null, "plan": "dedicated", "media_url": "", "message_type": "message", "group_id": "", "participants": [], "send_style": "", "opted_out": false, "error_detail": null, "sendblue_number": null, "service": "iMessage", "group_display_name": null } ``` You must send a 200-level response to acknowledge receipt of the webhook. This prevents Sendblue from sending duplicate webhook calls. ### Call Log Webhook When a call is logged (inbound or outbound), Sendblue will POST to your configured `call_log` webhook with the following payload: ``` { "event_type": "call_log", "call_id": "cs_abc123def456", "from_number": "+15551234567", "to_number": "+15559876543", "direction": "inbound", "status": "COMPLETED", "duration": 120, "provider": "twilio", "company_id": "550e8400-e29b-41d4-a716-446655440000", "contact_id": "660e8400-e29b-41d4-a716-446655440000", "recording_url": "https://api.twilio.com/recordings/RE1234.mp3", "transcript": "Hello, how can I help you today?", "metadata": {}, "created_at": "2026-02-07T12:00:00Z", "disposition": "connected", "origin": "DASHBOARD", "sent_by": "user@example.com" } ``` #### Call Log Payload Fields | Field | Type | Description | | -------------- | --------- | ---------------------------------------------------------------- | | event\_type | `string` | Always `"call_log"` | | call\_id | `string` | Sendblue call session identifier | | from\_number | `string` | E.164 formatted caller phone number | | to\_number | `string` | E.164 formatted called phone number | | direction | `string` | `"inbound"` or `"outbound"` | | status | `string` | Final status of the call (e.g. `COMPLETED`, `CANCELLED`) | | duration | `integer` | Call duration in seconds (null if not available) | | provider | `string` | Telephony provider (`"twilio"` or `"facetime"`) | | company\_id | `string` | UUID of the company | | contact\_id | `string` | UUID of the contact (null if not linked) | | recording\_url | `string` | URL of the call recording (null if not recorded) | | transcript | `string` | Call transcript (null if not transcribed) | | metadata | `object` | Additional metadata associated with the call | | created\_at | `string` | ISO 8601 timestamp of when the call was created | | disposition | `string` | Call disposition (e.g. `connected`, `not_answered`, `voicemail`) | | origin | `string` | Where the call originated from (e.g. `DASHBOARD`) | | sent\_by | `string` | Identifier of the user who initiated the call | ### Message 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 | | 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) | | error\_reason | `string` | Additional error context (null if no error) | | error\_detail | `string` | Detailed error information (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, null otherwise | | plan | `string` | Value of the Sendblue account plan | | media\_url | `string` | A CDN link to any media attached to the message | | message\_type | `string` | Type of message (e.g., “message”) | | group\_id | `string` | Group identifier (empty string for non-group messages) | | participants | `array` | Array of participant phone numbers | | send\_style | `string` | Expressive message style if used (empty string if none) | | opted\_out | `boolean` | True if the recipient has opted out | | sendblue\_number | `string` | The Sendblue phone number used | | service | `string` | The messaging service used (e.g., “iMessage”, “SMS”) | | group\_display\_name | `string` | Display name for group chats (null for non-group messages) | ## Best Practices ### 1. Use HTTPS Only All webhook URLs must use HTTPS to ensure secure communication. ### 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 handler const 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 Always verify that webhook requests are coming from Sendblue by checking the secret in the request headers. ### 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 Implement proper error handling and logging in your webhook endpoints to troubleshoot issues. ## Error Responses ### 401 Unauthorized ``` { "status": "ERROR", "message": "Unauthorized" } ``` Authentication failed. Check your API credentials. ### 400 Bad Request ``` { "status": "ERROR", "message": "Missing or invalid webhooks array" } ``` The request body is malformed or missing required fields. ### 500 Internal Server Error ``` { "status": "ERROR", "message": "Error message details" } ``` An internal error occurred. Contact support if this persists. ## Examples ### Example 1: Basic Webhook Setup ``` // Add a simple webhook for receiving messages const response = await fetch("https://api.sendblue.co/api/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 ``` // Configure webhooks for multiple event types const response = await fetch("https://api.sendblue.co/api/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 ``` 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"); }); ``` ## Retry Policy Sendblue retries webhook delivery up to **3 times** if your endpoint returns a 5xx server error. Sendblue waits **45 seconds** for a response from your endpoint. If no response is received within that window, the delivery is treated as failed and will be retried. Ensure your endpoint is idempotent to handle potential duplicate deliveries during retries. Use the `message_handle` field to deduplicate events. ## Limitations - **Account-level only**: Webhook URLs are configured at the account level. All lines on a single account share the same webhook endpoints. You cannot set a different webhook per phone number. - **No default status callback**: There is no way to set a global default `status_callback` webhook. The status callback URL must be specified on each individual message send request. See [Sending messages](/getting-started/sending-messages/#status-callback/index.md). - **Contact update webhooks**: Webhook events for contact updates (e.g., tags and notes) are not currently available. This is being evaluated for a future release. - **Webhook error logging**: Dedicated webhook error logging is coming soon and will be accessible for internal tracking and debugging. ## Additional Notes - Webhook URLs are automatically validated to ensure they are valid HTTPS URLs - The API maintains backward compatibility with legacy webhook formats - The `receive` webhook type is the most commonly used for inbound messages - You can configure multiple webhooks for the same event type for redundancy