DeckleDocs
Dashboard

API reference

Webhook events

The payloads Deckle delivers to your registered webhook endpoints.

When something happens to an email you sent, Deckle POSTs a signed event to every active webhook endpoint subscribed to that event type.

Request format#

Every delivery is an HTTP POST with a JSON body and two headers: X-Deckle-Event (the event type) and X-Deckle-Signature (an HMAC-SHA256 hex digest of the raw body — see Verifying signatures).

http
POST /your/webhook HTTP/1.1
Content-Type: application/json
X-Deckle-Event: email.delivered
X-Deckle-Signature: 3f9a1c...e7

Envelope#

Every event shares the same top-level shape:

json
{
  "type": "email.delivered",
  "created_at": "2026-07-10T14:23:45.123Z",
  "data": { }
}

Event types#

typeFired when
email.deliveredThe receiving mail server accepted the message.
email.bouncedA hard bounce. The address is also added to your suppression list.
email.complainedThe recipient marked it as spam. The address is suppressed.
email.openedThe tracking pixel was loaded.
email.clickedA tracked link was clicked.

The data object#

The data object identifies the email the event is about — its id, recipient, and subject — plus fields specific to the event.

email.delivered / opened#

json
{
  "type": "email.opened",
  "created_at": "2026-07-10T14:25:00.000Z",
  "data": {
    "id": "email_9f2c1a8b",
    "to": "jane@example.com",
    "subject": "Welcome to Acme"
  }
}

email.clicked#

Click events also include the url that was followed.

json
{
  "type": "email.clicked",
  "created_at": "2026-07-10T14:26:10.000Z",
  "data": {
    "id": "email_9f2c1a8b",
    "to": "jane@example.com",
    "subject": "Welcome to Acme",
    "url": "https://acme.com/get-started"
  }
}

email.bounced / complained#

json
{
  "type": "email.bounced",
  "created_at": "2026-07-10T14:24:30.000Z",
  "data": {
    "id": "email_9f2c1a8b",
    "to": "jane@example.com",
    "subject": "Welcome to Acme",
    "reason": "hard_bounce"
  }
}

Always respond 2xx quickly

Acknowledge the webhook with a 2xx status as soon as you’ve verified the signature, then do slow work asynchronously. Verify the signature against the raw body before trusting any field.