Webhooks

Configure project webhooks and inbound receiving routes, signatures, tests, and delivery history.

This reference is generated from the API’s routes and request validators. Download the OpenAPI specification. JSON bodies go directly to the API; MCP tools use a body object and a query object.

Generated request examples contain placeholders. Replace addresses, URLs, resource IDs and other placeholder values before sending. Conditional rules and refinements are enforced by the API at runtime.

GET/webhooks

list (webhooks)

Authentication: Secret project API key. Resources are scoped to that project.

MCP: list_webhooks

Project-scoped customer operation. Server enforces project ownership and additional cross-field validation. IDs are UUIDs, not prefixed placeholders.

Response codes: 200, 400, 401, 403, 404, 429, 500, 503. Errors do not imply that an uncertain send is safe to repeat. A successful sending response confirms API acceptance, not inbox delivery.

POST/webhooks

create (webhooks)

Authentication: Secret project API key. Resources are scoped to that project.

MCP: create_webhook

Request body: application/json

FieldRequiredType / constraints
urlYesstring
descriptionNo / conditionalstring
eventsNo / conditionalarray
inboundDomainNo / conditionalstring, nullable
inboundPatternNo / conditionalstring, nullable
enabledNo / conditionalboolean
Complete request JSON Schema
{
  "type": "object",
  "properties": {
    "url": {
      "type": "string",
      "minLength": 1
    },
    "description": {
      "type": "string",
      "maxLength": 500
    },
    "events": {
      "type": "array",
      "items": {
        "type": "string",
        "enum": [
          "email.sent",
          "email.opened",
          "email.clicked",
          "email.bounced",
          "email.complained",
          "email.unsubscribed",
          "email.received",
          "contact.created",
          "contact.updated",
          "contact.subscribed",
          "contact.unsubscribed",
          "contact.deleted",
          "campaign.scheduled",
          "campaign.sent",
          "campaign.cancelled"
        ]
      }
    },
    "inboundDomain": {
      "type": "string",
      "maxLength": 253,
      "nullable": true
    },
    "inboundPattern": {
      "type": "string",
      "maxLength": 64,
      "nullable": true
    },
    "enabled": {
      "type": "boolean"
    }
  },
  "required": [
    "url"
  ],
  "additionalProperties": false
}

Project-scoped customer operation. Server enforces project ownership and additional cross-field validation. IDs are UUIDs, not prefixed placeholders.

Response codes: 201, 400, 401, 403, 404, 429, 500, 503. Errors do not imply that an uncertain send is safe to repeat. A successful sending response confirms API acceptance, not inbox delivery.

GET/webhooks/:id

get (webhooks)

Authentication: Secret project API key. Resources are scoped to that project.

MCP: get_webhook

ParameterLocationRequiredType
idpathYesstring

Project-scoped customer operation. Server enforces project ownership and additional cross-field validation. IDs are UUIDs, not prefixed placeholders.

Response codes: 200, 400, 401, 403, 404, 429, 500, 503. Errors do not imply that an uncertain send is safe to repeat. A successful sending response confirms API acceptance, not inbox delivery.

PATCH/webhooks/:id

update (webhooks)

Authentication: Secret project API key. Resources are scoped to that project.

MCP: update_webhook

ParameterLocationRequiredType
idpathYesstring

Request body: application/json

FieldRequiredType / constraints
urlNo / conditionalstring
descriptionNo / conditionalstring
eventsNo / conditionalarray
inboundDomainNo / conditionalstring, nullable
inboundPatternNo / conditionalstring, nullable
enabledNo / conditionalboolean
rotateSecretNo / conditionalboolean
Complete request JSON Schema
{
  "type": "object",
  "properties": {
    "url": {
      "type": "string",
      "minLength": 1
    },
    "description": {
      "type": "string",
      "maxLength": 500
    },
    "events": {
      "type": "array",
      "items": {
        "type": "string",
        "enum": [
          "email.sent",
          "email.opened",
          "email.clicked",
          "email.bounced",
          "email.complained",
          "email.unsubscribed",
          "email.received",
          "contact.created",
          "contact.updated",
          "contact.subscribed",
          "contact.unsubscribed",
          "contact.deleted",
          "campaign.scheduled",
          "campaign.sent",
          "campaign.cancelled"
        ]
      }
    },
    "inboundDomain": {
      "type": "string",
      "maxLength": 253,
      "nullable": true
    },
    "inboundPattern": {
      "type": "string",
      "maxLength": 64,
      "nullable": true
    },
    "enabled": {
      "type": "boolean"
    },
    "rotateSecret": {
      "type": "boolean"
    }
  },
  "additionalProperties": false
}

Project-scoped customer operation. Server enforces project ownership and additional cross-field validation. IDs are UUIDs, not prefixed placeholders.

Response codes: 200, 400, 401, 403, 404, 429, 500, 503. Errors do not imply that an uncertain send is safe to repeat. A successful sending response confirms API acceptance, not inbox delivery.

DELETE/webhooks/:id

delete (webhooks)

Authentication: Secret project API key. Resources are scoped to that project.

MCP: delete_webhook

ParameterLocationRequiredType
idpathYesstring

Project-scoped customer operation. Server enforces project ownership and additional cross-field validation. IDs are UUIDs, not prefixed placeholders.

Response codes: 200, 400, 401, 403, 404, 429, 500, 503. Errors do not imply that an uncertain send is safe to repeat. A successful sending response confirms API acceptance, not inbox delivery.

GET/webhooks/:id/deliveries

deliveries (webhooks)

Authentication: Secret project API key. Resources are scoped to that project.

MCP: list_webhook_deliveries

ParameterLocationRequiredType
idpathYesstring

Project-scoped customer operation. Server enforces project ownership and additional cross-field validation. IDs are UUIDs, not prefixed placeholders.

Response codes: 200, 400, 401, 403, 404, 429, 500, 503. Errors do not imply that an uncertain send is safe to repeat. A successful sending response confirms API acceptance, not inbox delivery.

POST/webhooks/:id/test

test (webhooks)

Authentication: Secret project API key. Resources are scoped to that project.

MCP: test_webhook

ParameterLocationRequiredType
idpathYesstring

Request body: application/json

FieldRequiredType / constraints
eventNo / conditionalstring (test, email.received)
recipientNo / conditionalstring, email
Complete request JSON Schema
{
  "type": "object",
  "properties": {
    "event": {
      "type": "string",
      "enum": [
        "test",
        "email.received"
      ]
    },
    "recipient": {
      "type": "string",
      "maxLength": 320,
      "format": "email"
    }
  },
  "additionalProperties": false
}

Project-scoped customer operation. Server enforces project ownership and additional cross-field validation. IDs are UUIDs, not prefixed placeholders.

Response codes: 200, 400, 401, 403, 404, 429, 500, 503. Errors do not imply that an uncertain send is safe to repeat. A successful sending response confirms API acceptance, not inbox delivery.

GET/projects/:projectId/webhooks

list (projects/:projectId/webhooks)

Authentication: Secret project API key. Resources are scoped to that project.

MCP: Not exposed through remote MCP; account administration, credentials, browser tracking, and dashboard aliases retain their own authorization.

ParameterLocationRequiredType
projectIdpathYesstring

Dashboard alias; MCP uses canonical /webhooks. Server enforces project ownership and additional cross-field validation. IDs are UUIDs, not prefixed placeholders.

Response codes: 200, 400, 401, 403, 404, 429, 500, 503. Errors do not imply that an uncertain send is safe to repeat. A successful sending response confirms API acceptance, not inbox delivery.

POST/projects/:projectId/webhooks

create (projects/:projectId/webhooks)

Authentication: Secret project API key. Resources are scoped to that project.

MCP: Not exposed through remote MCP; account administration, credentials, browser tracking, and dashboard aliases retain their own authorization.

ParameterLocationRequiredType
projectIdpathYesstring

Request body: application/json

FieldRequiredType / constraints
urlYesstring
descriptionNo / conditionalstring
eventsNo / conditionalarray
inboundDomainNo / conditionalstring, nullable
inboundPatternNo / conditionalstring, nullable
enabledNo / conditionalboolean
Complete request JSON Schema
{
  "type": "object",
  "properties": {
    "url": {
      "type": "string",
      "minLength": 1
    },
    "description": {
      "type": "string",
      "maxLength": 500
    },
    "events": {
      "type": "array",
      "items": {
        "type": "string",
        "enum": [
          "email.sent",
          "email.opened",
          "email.clicked",
          "email.bounced",
          "email.complained",
          "email.unsubscribed",
          "email.received",
          "contact.created",
          "contact.updated",
          "contact.subscribed",
          "contact.unsubscribed",
          "contact.deleted",
          "campaign.scheduled",
          "campaign.sent",
          "campaign.cancelled"
        ]
      }
    },
    "inboundDomain": {
      "type": "string",
      "maxLength": 253,
      "nullable": true
    },
    "inboundPattern": {
      "type": "string",
      "maxLength": 64,
      "nullable": true
    },
    "enabled": {
      "type": "boolean"
    }
  },
  "required": [
    "url"
  ],
  "additionalProperties": false
}

Dashboard alias; MCP uses canonical /webhooks. Server enforces project ownership and additional cross-field validation. IDs are UUIDs, not prefixed placeholders.

Response codes: 201, 400, 401, 403, 404, 429, 500, 503. Errors do not imply that an uncertain send is safe to repeat. A successful sending response confirms API acceptance, not inbox delivery.

GET/projects/:projectId/webhooks/:id

get (projects/:projectId/webhooks)

Authentication: Secret project API key. Resources are scoped to that project.

MCP: Not exposed through remote MCP; account administration, credentials, browser tracking, and dashboard aliases retain their own authorization.

ParameterLocationRequiredType
projectIdpathYesstring
idpathYesstring

Dashboard alias; MCP uses canonical /webhooks. Server enforces project ownership and additional cross-field validation. IDs are UUIDs, not prefixed placeholders.

Response codes: 200, 400, 401, 403, 404, 429, 500, 503. Errors do not imply that an uncertain send is safe to repeat. A successful sending response confirms API acceptance, not inbox delivery.

PATCH/projects/:projectId/webhooks/:id

update (projects/:projectId/webhooks)

Authentication: Secret project API key. Resources are scoped to that project.

MCP: Not exposed through remote MCP; account administration, credentials, browser tracking, and dashboard aliases retain their own authorization.

ParameterLocationRequiredType
projectIdpathYesstring
idpathYesstring

Request body: application/json

FieldRequiredType / constraints
urlNo / conditionalstring
descriptionNo / conditionalstring
eventsNo / conditionalarray
inboundDomainNo / conditionalstring, nullable
inboundPatternNo / conditionalstring, nullable
enabledNo / conditionalboolean
rotateSecretNo / conditionalboolean
Complete request JSON Schema
{
  "type": "object",
  "properties": {
    "url": {
      "type": "string",
      "minLength": 1
    },
    "description": {
      "type": "string",
      "maxLength": 500
    },
    "events": {
      "type": "array",
      "items": {
        "type": "string",
        "enum": [
          "email.sent",
          "email.opened",
          "email.clicked",
          "email.bounced",
          "email.complained",
          "email.unsubscribed",
          "email.received",
          "contact.created",
          "contact.updated",
          "contact.subscribed",
          "contact.unsubscribed",
          "contact.deleted",
          "campaign.scheduled",
          "campaign.sent",
          "campaign.cancelled"
        ]
      }
    },
    "inboundDomain": {
      "type": "string",
      "maxLength": 253,
      "nullable": true
    },
    "inboundPattern": {
      "type": "string",
      "maxLength": 64,
      "nullable": true
    },
    "enabled": {
      "type": "boolean"
    },
    "rotateSecret": {
      "type": "boolean"
    }
  },
  "additionalProperties": false
}

Dashboard alias; MCP uses canonical /webhooks. Server enforces project ownership and additional cross-field validation. IDs are UUIDs, not prefixed placeholders.

Response codes: 200, 400, 401, 403, 404, 429, 500, 503. Errors do not imply that an uncertain send is safe to repeat. A successful sending response confirms API acceptance, not inbox delivery.

DELETE/projects/:projectId/webhooks/:id

delete (projects/:projectId/webhooks)

Authentication: Secret project API key. Resources are scoped to that project.

MCP: Not exposed through remote MCP; account administration, credentials, browser tracking, and dashboard aliases retain their own authorization.

ParameterLocationRequiredType
projectIdpathYesstring
idpathYesstring

Dashboard alias; MCP uses canonical /webhooks. Server enforces project ownership and additional cross-field validation. IDs are UUIDs, not prefixed placeholders.

Response codes: 200, 400, 401, 403, 404, 429, 500, 503. Errors do not imply that an uncertain send is safe to repeat. A successful sending response confirms API acceptance, not inbox delivery.

GET/projects/:projectId/webhooks/:id/deliveries

deliveries (projects/:projectId/webhooks)

Authentication: Secret project API key. Resources are scoped to that project.

MCP: Not exposed through remote MCP; account administration, credentials, browser tracking, and dashboard aliases retain their own authorization.

ParameterLocationRequiredType
projectIdpathYesstring
idpathYesstring

Dashboard alias; MCP uses canonical /webhooks. Server enforces project ownership and additional cross-field validation. IDs are UUIDs, not prefixed placeholders.

Response codes: 200, 400, 401, 403, 404, 429, 500, 503. Errors do not imply that an uncertain send is safe to repeat. A successful sending response confirms API acceptance, not inbox delivery.

POST/projects/:projectId/webhooks/:id/test

test (projects/:projectId/webhooks)

Authentication: Secret project API key. Resources are scoped to that project.

MCP: Not exposed through remote MCP; account administration, credentials, browser tracking, and dashboard aliases retain their own authorization.

ParameterLocationRequiredType
projectIdpathYesstring
idpathYesstring

Request body: application/json

FieldRequiredType / constraints
eventNo / conditionalstring (test, email.received)
recipientNo / conditionalstring, email
Complete request JSON Schema
{
  "type": "object",
  "properties": {
    "event": {
      "type": "string",
      "enum": [
        "test",
        "email.received"
      ]
    },
    "recipient": {
      "type": "string",
      "maxLength": 320,
      "format": "email"
    }
  },
  "additionalProperties": false
}

Dashboard alias; MCP uses canonical /webhooks. Server enforces project ownership and additional cross-field validation. IDs are UUIDs, not prefixed placeholders.

Response codes: 200, 400, 401, 403, 404, 429, 500, 503. Errors do not imply that an uncertain send is safe to repeat. A successful sending response confirms API acceptance, not inbox delivery.

Webhooks deliver real-time notifications to your server when events occur. Sendbound sends an HTTP POST to your endpoint for each matching event.

Webhook subscriptions are scoped to the project authenticated by your secret API key. Use /webhooks for API integrations. The dashboard also uses the equivalent /projects/:projectId/webhooks endpoints with its authenticated project session.

For MX configuration, catch-all receiving routes, bodies, attachment downloads, V2 signature verification, retry timing, and realistic tests, see the Inbound email guide in the Concepts section.

Webhook object

{
  "id": "c856112f-3a02-5402-a6f5-a702a637dc01",
  "url": "https://yourapp.com/hooks/sendbound",
  "description": "Production delivery events",
  "events": ["email.received"],
  "inboundDomain": "inbound.example.com",
  "inboundPattern": "*",
  "enabled": true,
  "createdAt": "2026-01-20T09:00:00Z"
}

Event types

EventTrigger
email.sentEmail sent through the configured transport; not proof of inbox placement
email.receivedEmail received and parsed for this project
email.openedRecipient opened the email
email.clickedRecipient clicked a tracked link
email.bouncedPermanent delivery failure
email.complainedRecipient marked the email as spam
contact.createdNew contact added
contact.updatedContact updated
contact.subscribedContact subscribed
contact.unsubscribedContact unsubscribed
contact.deletedContact deleted
email.unsubscribedEmail unsubscribe event
campaign.scheduledCampaign scheduled
campaign.sentCampaign sent
campaign.cancelledCampaign cancelled

Webhook payload

Bodies are event-specific JSON objects, not a standard {event, timestamp, data} envelope. Read the event name from Sendbound-Event. Verify the raw body, timestamp, and delivery ID with Sendbound-Signature-V2 before processing. An inbound example:

{
  "id": "c856112f-3a02-5402-a6f5-a702a637dc01",
  "from": "sender@example.net",
  "to": "jane@inbound.example.com",
  "recipients": ["jane@inbound.example.com"],
  "subject": "Example email",
  "text": "Hello",
  "html": "<p>Hello</p>",
  "messageId": "<original@example.net>",
  "receivedAt": "2026-10-16T12:00:00.000Z",
  "attachments": []
}

List webhooks

GET/webhooks

Returns all webhook subscriptions for the project.

Create a webhook

POST/webhooks

Subscribe to one or more event types. The signing secret is returned only in this response — store it securely.

FieldTypeRequiredDescription
urlstringYesYour HTTPS endpoint URL
descriptionstringNoHuman-readable label
eventsstring[]NoEvent types to subscribe to. Omit to receive all events.
inboundDomainstringNoExact receiving domain; requires verified ownership of this domain or a parent in the same project.
inboundPatternstringNo* (default) or an exact local part. A bound route must subscribe only to email.received.
enabledbooleanNoDefaults to true.

Get a webhook

GET/webhooks/:id

Returns a single webhook subscription by ID.

Update a webhook

PATCH/webhooks/:id

Update the URL, description, event list, or enabled state. Only fields you include are changed. Pass `rotateSecret: true` to generate a new signing secret.

Delete a webhook

DELETE/webhooks/:id

Removes the webhook subscription and delivery history. Returns 200 with success: true. Does not alter your DNS.

Test a webhook

POST/webhooks/:id/test

Sends a one-shot signed test. For email.received subscriptions, parses a synthetic MIME email and sends the complete inbound payload. This does not test external DNS or SMTP.

For inbound tests, optional recipient must match the saved receiving domain and address pattern. Use an address your application recognizes. Omitted recipients default to the saved exact address, or test@DOMAIN for a catch-all. Tests are synthetic (test: true), one-shot and do not confirm DNS or SMTP. Failed delivery returns HTTP 502; the error reports the receiving endpoint's HTTP status, and delivery history includes its response body. Verify signatures before acknowledging synthetic tests; never bypass authentication.

Verifying signatures

New integrations should verify Sendbound-Signature-V2. Its value is v2=<hex HMAC-SHA256>, using the signing-secret string and the exact input timestamp + "." + deliveryId + "." + rawBody. The timestamp is Unix milliseconds. Check timestamp freshness and deduplicate by Sendbound-Delivery-Id; retries retain that ID but have fresh timestamps.

import crypto from 'crypto';

function verifyWebhook(rawBody, signatureHeader, secret, timestamp, deliveryId) {
  if (!/^\d+$/.test(timestamp) || !deliveryId ||
      Math.abs(Date.now() - Number(timestamp)) > 300000 ||
      !/^v2=[0-9a-f]{64}$/.test(signatureHeader)) return false;
  const expected = crypto
    .createHmac('sha256', secret)
    .update(`${timestamp}.${deliveryId}.`).update(rawBody).digest();
  const supplied = Buffer.from(signatureHeader.slice(3), 'hex');
  return expected.length === supplied.length && crypto.timingSafeEqual(expected, supplied);
}

The signing secret is returned once when you create the webhook. If you lose it, rotate it with PATCH using rotateSecret: true.

For compatibility, Sendbound-Signature: sha256=<hex> still signs only the raw body. It does not authenticate the timestamp or delivery ID.

Delivery history and retries

GET /webhooks/:id/deliveries returns recent status, attempts, HTTP response code, and response text. Use ?before=<ISO timestamp> for earlier deliveries. The webhook detail endpoint also includes recent deliveries.

Production jobs make eight total attempts, with exponential waits of 30, 60, 120, 240, 480, 960, and 1920 seconds. Each HTTP attempt times out after ten seconds. Network failures, timeouts, redirects, and non-2xx responses fail the attempt. Tests are one-shot and are not retried.

Select an endpoint
to see request details