Webhook Endpoints API

The Webhook Endpoints API provides full CRUD (Create, Read, Update, Delete) operations for managing webhook endpoint configurations. You can create, list, view, update, delete webhook endpoints, send test webhooks, and view delivery history programmatically.

Webhook endpoints allow you to receive real-time notifications about events in your Broadcast channel, such as email deliveries, subscriber changes, and broadcast status updates.

Required Permissions

All endpoints require authentication via an API token with appropriate permissions:

  • Read Permission: Required for GET endpoints (list, show, deliveries)
  • Write Permission: Required for POST, PATCH, DELETE endpoints (create, update, delete, test)

Webhook Endpoint Object

The webhook endpoint object contains the following fields:

Field Description
`id` Unique identifier of the webhook endpoint
`url` The HTTPS URL where webhooks will be sent
`description` Optional description of the webhook endpoint
`active` Whether the webhook endpoint is active
`event_types` Array of event types this endpoint subscribes to
`retries_to_attempt` Number of retry attempts for failed deliveries (default: 6)
`secret` HMAC secret for signature verification (redacted after creation)
`last_response_code` HTTP status code from the last webhook delivery
`last_sent_at` Timestamp of the last webhook delivery attempt
`created_at` When the webhook endpoint was created
`updated_at` When the webhook endpoint was last updated

Security Note

The webhook secret is used to sign webhook payloads using HMAC-SHA256. For security:

  • The secret is only shown in full when creating a new webhook endpoint
  • In subsequent GET requests, the secret is redacted (e.g., abcd••••••••wxyz)
  • Store the secret securely when you receive it during creation
  • The secret cannot be modified; create a new webhook endpoint if you need a new secret

Available Event Types

You can subscribe to any combination of the following event types:

Email Events

Event Type Description
email.sent Email was successfully sent to the mail server
email.delivered Email was delivered to the recipient’s inbox
email.delivery_delayed Email delivery is delayed
email.opened Recipient opened the email
email.clicked Recipient clicked a link in the email
email.bounced Email bounced (hard or soft bounce)
email.complained Recipient marked the email as spam
email.failed Email failed to send

Subscriber Events

Event Type Description
subscriber.created New subscriber was added
subscriber.updated Subscriber data was updated
subscriber.deleted Subscriber was deleted
subscriber.subscribed Subscriber opted in
subscriber.unsubscribed Subscriber opted out
subscriber.bounced Subscriber marked as bounced
subscriber.complained Subscriber marked as complained

Broadcast Events

Event Type Description
broadcast.scheduled Broadcast was scheduled for sending
broadcast.queueing Broadcast is being queued for sending
broadcast.sending Broadcast is actively sending
broadcast.sent Broadcast completed sending
broadcast.failed Broadcast failed to send
broadcast.partial_failure Some emails failed during broadcast
broadcast.aborted Broadcast was aborted
broadcast.paused Broadcast was paused

Sequence Events

Event Type Description
sequence.subscriber_added Subscriber was added to a sequence
sequence.subscriber_completed Subscriber completed a sequence
sequence.subscriber_moved Subscriber was moved in a sequence
sequence.subscriber_removed Subscriber was removed from a sequence
sequence.subscriber_paused Subscriber was paused in a sequence
sequence.subscriber_resumed Subscriber was resumed in a sequence
sequence.subscriber_error Error occurred with subscriber in sequence

Other Events

Event Type Description
message.attempt.exhausted All retry attempts for a webhook delivery have been exhausted
test.webhook Test webhook event

List Webhook Endpoints

GET /api/v1/webhook_endpoints

Retrieve a list of all webhook endpoints for the authenticated broadcast channel.

Query Parameters

  • limit (optional): Maximum number of endpoints to return
  • offset (optional): Number of endpoints to skip for pagination

Request

curl -X GET \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  https://your-domain.com/api/v1/webhook_endpoints

Response

{
  "data": [
    {
      "id": 1,
      "url": "https://api.example.com/webhooks",
      "description": "Production webhook endpoint",
      "active": true,
      "event_types": ["email.sent", "email.delivered", "email.bounced"],
      "retries_to_attempt": 6,
      "secret": "abcd••••••••••••••••••••••••wxyz",
      "last_response_code": 200,
      "last_sent_at": "2024-01-15T10:30:00Z",
      "created_at": "2024-01-01T12:00:00Z",
      "updated_at": "2024-01-15T10:30:00Z"
    }
  ],
  "total": 1
}

Get Webhook Endpoint

GET /api/v1/webhook_endpoints/:id

Retrieve details of a specific webhook endpoint.

Request

curl -X GET \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  https://your-domain.com/api/v1/webhook_endpoints/1

Response

{
  "id": 1,
  "url": "https://api.example.com/webhooks",
  "description": "Production webhook endpoint",
  "active": true,
  "event_types": ["email.sent", "email.delivered", "email.bounced"],
  "retries_to_attempt": 6,
  "secret": "abcd••••••••••••••••••••••••wxyz",
  "last_response_code": 200,
  "last_sent_at": "2024-01-15T10:30:00Z",
  "created_at": "2024-01-01T12:00:00Z",
  "updated_at": "2024-01-15T10:30:00Z"
}

Create Webhook Endpoint

POST /api/v1/webhook_endpoints

Create a new webhook endpoint.

Required Parameters

  • url (required): A valid HTTPS URL where webhooks will be sent
  • event_types (required): Array of event types to subscribe to (at least one)

Optional Parameters

  • description: Description of the webhook endpoint
  • active: Whether the endpoint is active (default: true)
  • retries_to_attempt: Number of retry attempts (default: 6)

Request

curl -X POST \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "webhook_endpoint": {
      "url": "https://api.example.com/webhooks",
      "description": "Production webhook endpoint",
      "active": true,
      "event_types": ["email.sent", "email.delivered", "email.bounced", "subscriber.created"],
      "retries_to_attempt": 6
    }
  }' \
  https://your-domain.com/api/v1/webhook_endpoints

Response

Note: The secret is shown in full only on creation. Store it securely!

{
  "id": 1,
  "url": "https://api.example.com/webhooks",
  "description": "Production webhook endpoint",
  "active": true,
  "event_types": ["email.sent", "email.delivered", "email.bounced", "subscriber.created"],
  "retries_to_attempt": 6,
  "secret": "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6",
  "last_response_code": null,
  "last_sent_at": null,
  "created_at": "2024-01-01T12:00:00Z",
  "updated_at": "2024-01-01T12:00:00Z"
}

Update Webhook Endpoint

PATCH /api/v1/webhook_endpoints/:id

Update an existing webhook endpoint.

Request

curl -X PATCH \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "webhook_endpoint": {
      "description": "Updated description",
      "active": false,
      "event_types": ["email.sent", "email.delivered"]
    }
  }' \
  https://your-domain.com/api/v1/webhook_endpoints/1

Response

{
  "id": 1,
  "url": "https://api.example.com/webhooks",
  "description": "Updated description",
  "active": false,
  "event_types": ["email.sent", "email.delivered"],
  "retries_to_attempt": 6,
  "secret": "a1b2••••••••••••••••••••••••y5z6",
  "last_response_code": 200,
  "last_sent_at": "2024-01-15T10:30:00Z",
  "created_at": "2024-01-01T12:00:00Z",
  "updated_at": "2024-01-15T11:00:00Z"
}

Delete Webhook Endpoint

DELETE /api/v1/webhook_endpoints/:id

Delete a webhook endpoint.

Request

curl -X DELETE \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  https://your-domain.com/api/v1/webhook_endpoints/1

Response

{
  "message": "Webhook endpoint deleted successfully"
}

Send Test Webhook

POST /api/v1/webhook_endpoints/:id/test

Send a test webhook to verify your endpoint is working correctly. The test webhook will contain realistic sample data for the specified event type.

Query Parameters

  • event_type (optional): The event type to test (default: “test.webhook”). Must be one of the event types configured for this endpoint.

Request

curl -X POST \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "event_type": "email.delivered"
  }' \
  https://your-domain.com/api/v1/webhook_endpoints/1/test

Response

{
  "message": "Test webhook queued for delivery",
  "event_type": "email.delivered",
  "delivery_id": 42
}

List Webhook Deliveries

GET /api/v1/webhook_endpoints/:id/deliveries

Retrieve the delivery history for a webhook endpoint, including successful deliveries and failed attempts.

Query Parameters

  • limit (optional): Maximum number of deliveries to return
  • offset (optional): Number of deliveries to skip for pagination

Request

curl -X GET \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  https://your-domain.com/api/v1/webhook_endpoints/1/deliveries

Response

{
  "data": [
    {
      "id": 42,
      "event_type": "email.delivered",
      "event_data": {
        "receipt_id": 1234,
        "email": "[email protected]",
        "delivered": true,
        "opened": false
      },
      "attempt_number": 1,
      "response_status": 200,
      "response_body": "OK",
      "error_message": null,
      "first_attempt_at": "2024-01-15T10:30:00Z",
      "last_attempt_at": "2024-01-15T10:30:00Z",
      "next_retry_at": null,
      "successfully_delivered_at": "2024-01-15T10:30:00Z",
      "created_at": "2024-01-15T10:30:00Z",
      "updated_at": "2024-01-15T10:30:00Z"
    }
  ],
  "total": 1
}

Webhook Delivery Object

The delivery object contains information about each webhook delivery attempt:

Field Description
`id` Unique identifier of the delivery
`event_type` The type of event that triggered this delivery
`event_data` The payload that was sent to the webhook URL
`attempt_number` Current attempt number (1-6)
`response_status` HTTP status code from your endpoint
`response_body` Response body from your endpoint
`error_message` Error message if delivery failed
`first_attempt_at` When the first delivery attempt was made
`last_attempt_at` When the most recent attempt was made
`next_retry_at` When the next retry will be attempted (if applicable)
`successfully_delivered_at` When delivery was successful (null if not yet delivered)

Retry Schedule

Failed webhook deliveries are automatically retried with exponential backoff:

Attempt Delay
1 Immediate
2 5 seconds
3 5 minutes
4 30 minutes
5 2 hours
6 5 hours

After 6 failed attempts, the delivery is marked as failed and a message.attempt.exhausted event is triggered.

Verifying Webhook Signatures

All webhooks are signed using HMAC-SHA256. To verify a webhook:

  1. Extract the signature from the broadcast-webhook-signature header
  2. Extract the timestamp from the broadcast-webhook-timestamp header
  3. Create the signed payload: {timestamp}.{raw_request_body}
  4. Calculate HMAC-SHA256 using your secret and the signed payload
  5. Compare the calculated signature with the one in the header

Header Format

broadcast-webhook-signature: v1,{base64_encoded_signature}
broadcast-webhook-timestamp: 1705312200

Example Verification (Ruby)

def verify_webhook(payload, headers, secret)
  signature_header = headers['broadcast-webhook-signature']
  timestamp = headers['broadcast-webhook-timestamp']

  version, signature = signature_header.split(',')
  return false unless version == 'v1'

  # Check timestamp (5 minute window)
  return false if (Time.now.to_i - timestamp.to_i).abs > 300

  signed_payload = "#{timestamp}.#{payload}"
  expected = Base64.strict_encode64(
    OpenSSL::HMAC.digest('sha256', secret, signed_payload)
  )

  ActiveSupport::SecurityUtils.secure_compare(signature, expected)
end

Error Responses

401 Unauthorized

{
  "error": "Unauthorized"
}

Returned when the API token is missing, invalid, or lacks the required permissions.

404 Not Found

{
  "error": "Webhook endpoint not found"
}

Returned when the specified webhook endpoint doesn’t exist or belongs to a different channel.

422 Unprocessable Entity

{
  "error": "Url is invalid, Event types must have one selected"
}

Common validation errors: - Missing or invalid URL (must be HTTP or HTTPS) - No event types selected - Duplicate URL (each URL must be unique per channel) - Invalid event type for test webhook

Example: Complete Setup Flow

# 1. Create a webhook endpoint
curl -X POST \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "webhook_endpoint": {
      "url": "https://api.myapp.com/broadcast-webhooks",
      "description": "Email event notifications",
      "event_types": ["email.sent", "email.delivered", "email.bounced", "email.opened"]
    }
  }' \
  https://your-domain.com/api/v1/webhook_endpoints

# Response: Save the secret! It won't be shown again.
# { "id": 1, "secret": "abc123...", ... }

# 2. Send a test webhook to verify your endpoint
curl -X POST \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"event_type": "email.delivered"}' \
  https://your-domain.com/api/v1/webhook_endpoints/1/test

# 3. Check the delivery history
curl -X GET \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  https://your-domain.com/api/v1/webhook_endpoints/1/deliveries

# 4. Verify successful delivery
# Response shows response_status: 200 and successfully_delivered_at is set