Skip to content

Email webhooks you can verify, retry and replay.

Know when an email is delivered, bounces or draws a complaint, with events your endpoint can trust and does not silently miss.

  • No card needed to test
  • Prices in euros, excluding VAT
verify.js
import { createHmac, timingSafeEqual } from "node:crypto";

export function verify(request, rawBody, secret) {
    const id = request.headers["outstamp-id"];
    const timestamp = request.headers["outstamp-timestamp"];
    const header = request.headers["outstamp-signature"] ?? "";

    if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false;

    const expected = createHmac("sha256", secret)
        .update(`${id}.${timestamp}.${rawBody}`)
        .digest("hex");

    return header.split(",")
        .map((part) => part.trim().replace(/^v1=/, ""))
        .some((candidate) => candidate.length === expected.length &&
            timingSafeEqual(Buffer.from(candidate, "hex"), Buffer.from(expected, "hex")));
}

What every delivery carries.

  • A signature over the raw body

    Outstamp-Signature: v1= is an HMAC-SHA256 of the delivery id, the timestamp and the exact body. Reject anything older than 5 minutes.

  • Retries for 48 hours

    10 attempts: at once, then after 5 seconds, 1, 5 and 30 minutes, and 2, 6, 12, 24 and 48 hours.

  • Replay for 30 days

    Replay any event from the dashboard or the API. It gets a new delivery id and keeps its event id.

  • Rotation without downtime

    When you rotate the secret, both the old and the new signature are sent for 24 hours.

Events you can subscribe to.

Events you can subscribe to.
GroupEventsTypical use
Deliveryemail.sent, email.delivered, email.delivery_delayedMark a message as delivered in your app
Problemsemail.bounced, email.complained, email.rejected, email.failed, email.suppressedStop emailing an address, or alert support
Lifecycleemail.accepted, email.scheduled, email.queued, email.canceled, email.submission_unknownAudit trails and scheduled sends
Domainsdomain.created, domain.status_changed, domain.deletedKnow when DNS breaks
Projectsuppression.created, suppression.deleted, project.quota_warning, project.reputation_warning, project.sending_paused, project.sending_resumedOperational alerts

Plan for these before you build.

They are the rules of any honest webhook system.

  • Delivery is at least once: deduplicate on the event id.
  • Order is not guaranteed: email.delivered can arrive before email.sent.
  • Answer with any 2xx within 10 seconds, and do the work afterwards.
  • After 20 failures in a row the endpoint is disabled, and a 410 answer disables it at once.

Receive your first event.

  1. 1

    Add an endpoint

    A public HTTPS URL and the event types you want. The signing secret is shown once.

  2. 2

    Verify the signature

    Use the raw body, compare in constant time and reject old timestamps.

  3. 3

    Trigger a test event

    Send to [email protected] with a test key, or use the test button in the dashboard.

Questions about webhooks.

Are webhooks signed?

Yes. Every delivery carries Outstamp-Signature: v1=, an HMAC-SHA256 of the delivery id, the timestamp and the raw body.

What if my endpoint is down?

We retry 10 times over 48 hours. After 20 failures in a row the endpoint is disabled, and you can replay what it missed for 30 days.

Do test emails trigger webhooks?

Yes. Test sends produce the same events and webhooks as live ones, marked as test.

Receive your first signed event today.

Test keys and the simulator are free, with no card.

  • No card needed to test
  • Prices in euros, excluding VAT