---
title: "Email event webhooks"
description: "Signed POSTs to your https endpoint when mail arrives, goes out, bounces or is opened. 51 events, retried for hours, ten endpoints per mailbox."
url: "https://nextmail.sh/features/webhooks"
---

[Developers](https://nextmail.sh/developers/features.md#events)

# Webhooks Told when it happens, not when you ask.

Register an https endpoint and get a signed POST when mail arrives, goes out, bounces or is opened.

[Open mail](https://nextmail.sh/mail/inbox) [Get Started](https://nextmail.sh/signup.md)[Developer features](https://nextmail.sh/developers/features.md)

In short

## What is an email webhook?

A webhook is an HTTP request a service sends to your URL when something happens, so your code reacts instead of polling. For email, that means mail arriving, leaving, bouncing or being opened.

51

events to subscribe to

10

endpoints per mailbox

8

attempts per event at most

100

failed events in a row disable an endpoint

How it works

## Every way of sending, one event

The composer, a send scheduled for Tuesday and the API all raise email.sent. Scheduling raises email.scheduled first.

## Signed, and yours to check

Every POST carries an HMAC-SHA-256 of the timestamp and the raw body. Check the bytes as they arrived and refuse anything over 300 seconds old.

## Retried only when it might work

Timeouts, 408, 425, 429 and 5xx are retried after 1, 5 and 30 minutes, then 2, 5, 10 and 10 hours. Any other 4xx is final, and 410 switches the endpoint off. Whatever still failed can be replayed, one event at a time, from the app or over the API.

## 51 events in 13 families

Choosing none subscribes to every message event except email.replied.

email.received Arrived email.replied Reply arrived email.sent Accepted for sending email.failed Could not be sent email.cancelled Taken back email.scheduled Set for later email.queued Undo window running email.delivered Receiver accepted email.delivery\_delayed Delayed, retrying email.bounced Came back email.complained Reported as spam email.suppressed Skipped, address suppressed email.opened Opened email.clicked Link followed email.downloaded File fetched domain.verified Now receiving domain.sending\_changed Sending verdict moved domain.deleted Removed suppression.added Address added suppression.removed Address allowed again file.uploaded File uploaded file.deleted File deleted form.submitted Someone signed up form.confirmed Sign-up confirmed automation.entered Contact entered automation.exited Contact left automation.paused Automation paused automation.resumed Automation back on automation.webhook Webhook step reached contact.created Contact added contact.updated Details changed contact.deleted Contact deleted contact.unsubscribed Unsubscribed contact.resubscribed Subscribed again sending.paused Sending paused sending.resumed Sending back on connector.connected An app is connected for the workspace connector.disconnected A workspace connector is disconnected connector.needs\_attention A workspace connector stops working and has to be reconnected task.run.completed A run of a task finishes its work task.run.failed A run of a task fails task.run.cancelled A run of a task is stopped before it finishes approval.requested A run of a task waits for a person to allow an action booking.created Someone books a time on a booking page booking.confirmed A booking that waited for approval is accepted booking.rescheduled A booking moves to another time booking.cancelled A booking is cancelled or declined broadcast.sending A broadcast starts going out broadcast.sent Every copy of a broadcast has been handed over for sending broadcast.failed A broadcast stops because it could not go out broadcast.cancelled A broadcast is cancelled before it finishes

## Verify a delivery

The SDK checks the HMAC in constant time and refuses deliveries over five minutes old.

Pass the raw body, not parsed JSON.

```
import { verifyWebhookSignature } from '@nextmail/sdk' export default async (request: Request) => {  const event = await verifyWebhookSignature({    payload: await request.text(),    headers: request.headers,    secret: process.env.NEXTMAIL_WEBHOOK_SECRET!  })   console.log(event.type, event.data)   return new Response(null, { status: 204 })}
```

What you get

## In the product today

Five seconds, in parallel

Endpoints are called at once, so ten still take five seconds.

Public https only

Private and loopback hosts are refused, and a redirect is never followed.

Every attempt logged

Each one shows what your server answered and how long it took.

Good practice

## Getting the most out of it

1. 01
   
   Answer, then work
   
   Return a 2xx within five seconds and do slow work afterwards.
2. 02
   
   Drop repeated ids
   
   Store each X-OpenEmail-Delivery id you handle and skip repeats.
3. 03
   
   Rotate without a gap
   
   After a rotation the old secret keeps signing for 24 hours, so deploy the new one within the day.

Questions

## Asked often

Keep going

## Works well with

REST API

A documented HTTP API with issuable, scopable, revocable keys.

Typed SDKs

TypeScript, Python, Ruby, PHP, Go, Java and C# clients, one method for every operation.

See when mail you sent was opened

Whether a message you sent has been opened, when, how many times and which of its links were followed. Off until you turn it on, and plain about the readings it cannot get.

Start

## Your domain, your mail.

Point a domain at Nextmail and read it in a mailbox built around it. The free plan covers one domain.

[Open mail](https://nextmail.sh/mail/inbox) [Get Started](https://nextmail.sh/signup.md)[Read the reference](https://nextmail.sh/docs/knowledge/developers/webhooks.md)
