Skip to content
Webhooks

Signed webhooks for every email event

Deliveries, bounces, complaints, opens and clicks reach your endpoint seconds after they happen. Every request is signed, retried when you do not answer, and kept so you can send it again.

Verify a delivery

// app/api/hooks/rasket/route.tsimport { verify } from "@rasket/webhook-verify";
export async function POST(request: Request) {  const rawBody = await request.text(); // text(), never json()
  try {    const event = verify(rawBody, request.headers, process.env.RASKET_WEBHOOK_SECRET);    await handle(event);  } catch {    return new Response("invalid signature", { status: 400 });  }
  return new Response("ok", { status: 200 });}
What a delivery carries

What every webhook delivery carries

An endpoint is a URL, a list of event types and a signing secret. Everything below is true of every request we make to it.

  • A signature on every request

    Three headers travel with the body: the event's id, a timestamp, and one or more signatures. The signature covers the raw bytes we sent, so a rewritten payload or a stale timestamp never verifies.

  • Twenty-three event types

    Each endpoint subscribes to the types it wants. Subscribing to one whose producer arrives in a later release is allowed and simply never fires.

  • Ten attempts, backing off

    Anything that is not a 2xx within ten seconds is a failed delivery. We try again up to ten times, spread over roughly a day, with jitter on every delay.

  • Replay the exact bytes

    Fix your handler, then send the event again from the dashboard or the API. A replay carries the identical payload under the same event id.

  • Rotate the secret with an overlap

    A new signing secret runs beside the old one for 24 hours and both sign, so a handler that accepts any matching signature needs no coordinated deploy.

How many endpoints a team may have is part of its plan. See what each plan includes.

How signed webhooks work

Three steps, and the middle one is the only one worth being careful about.

  1. 1

    Create an endpoint and pick its events

    Give us a URL and the event types you want it to receive. We hand back a signing secret beginning whsec_, shown once, and nothing is delivered anywhere else.

  2. 2

    Read the raw body, then verify

    Check that svix-timestamp is within five minutes, recompute the signature over the exact bytes, and compare it with svix-signature. Anything that parses the JSON first has already destroyed what was signed.

  3. 3

    Answer 2xx, then do your work

    Store the event and reply. Work finished before the reply eventually times out and is retried. Dedupe on svix-id, and do not rely on arrival order.

Every event type you can subscribe to

Twenty-three types today, grouped by what they are about. The envelope is the same for all of them: a type, the time it happened, and a data object.

  • Email delivery

    What happened to a message you sent, from the moment it is accepted.

    • email.sent
    • email.scheduled
    • email.delivered
    • email.delivery_delayed
    • email.bounced
    • email.complained
    • email.opened
    • email.clicked
    • email.failed
    • email.suppressed
    • email.canceled
  • Domains

    A sending domain added, changed or removed, including one that arrives through a claim.

    • domain.created
    • domain.updated
    • domain.deleted
  • Suppressions

    An address added to your suppression list, or taken off it.

    • suppression.added
    • suppression.removed
  • Contacts

    A contact created, changed or erased through the API. An import sends none of these.

    • contact.created
    • contact.updated
    • contact.deleted
  • Received mail

    Mail that arrived at one of your receiving addresses, with the envelope and the attachment list.

    • email.received
  • Automation runs

    A run enrolled a contact, reached the end of its graph, or ended without finishing.

    • automation.run.started
    • automation.run.completed
    • automation.run.failed

What happens when your endpoint is down

Nothing is dropped while you are fixing something. A failed delivery is recorded, retried, and left where you can replay it.

A delivery fails if it is not a 2xx within ten seconds: a timeout, a refused connection, a DNS or TLS error, or a redirect, which we never follow. We then try again up to ten times, with about ten percent of jitter on each delay so a recovering outage is not hit by everything at once.

The delay before each attempt
AttemptDelay before it
1immediately
25 seconds
330 seconds
42 minutes
510 minutes
630 minutes
71 hour
82 hours
94 hours
108 hours

After the tenth attempt we stop and mark the event failed. It is not lost: the payload we signed and every attempt we made stay readable, and you can replay it once your handler is right. An endpoint with no successful delivery for five days running is switched off and your team's admins are emailed. Events keep being recorded while it is off, so the backlog is still there to deliver when you turn it back on.

Questions about webhooks

Do I have to use your library to verify a signature?

No. The scheme is HMAC-SHA256 over the event id, the timestamp and the raw body, and it is compatible with the Svix libraries, so the official Python, Go, Ruby and PHP ones verify our deliveries as they are. For Node and TypeScript, @rasket/webhook-verify is one function with no dependencies of its own, and the Node SDK bundles the same verifier.

Are webhook events delivered in order?

No. A delivered event can arrive before the sent event that logically precedes it. Key your handler on the email id, the type and the event's own created_at rather than on the order things turn up in.

Can the same event arrive twice?

Yes, and a handler has to expect it. Dedupe on the svix-id header: it is stable for the life of an event and does not change when the event is retried or replayed.

How many endpoints can I have?

The number of webhook endpoints is part of your plan, and every plan includes some. The pricing page lists what each one allows.

What happens if my handler is slow?

Anything that is not a 2xx within ten seconds counts as a failure and is retried. Store the event, answer 2xx, and do the work afterwards. A redirect is a failure too, because we never follow one.

Can I see what was actually sent to my endpoint?

Yes. Every event keeps the exact payload we signed, along with every attempt we made, the status code that came back and the first 8 KB of your response. Both are readable on the dashboard and through the API.

Start listening to your email

Create an endpoint, subscribe it to the events you care about, and verify the first delivery in a few lines.