Skip to main content
In Sequence, you can listen to various events to trigger your own workflows. These are called webhooks. Sequence sends webhooks when something changes in your account - customers, invoices, billing schedules, credit notes, quotes. Point a webhook at your endpoint, and Sequence will POST the event data there whenever one of those changes occurs. For the full event catalog, payload envelope, signature format, and delivery behaviour, see Webhook events.

Quickstart

This section walks you through setting up an endpoint, configuring a webhook in Sequence, and confirming a test delivery.

1. Spin up an endpoint

The fastest way to start is webhook.site. It gives you a public URL and shows every request that lands. Copy your unique URL. If you want to receive webhooks on your machine, set up a local server and use a tunnel like ngrok to receive webhooks locally.

2. Create a webhook

Webhooks in the Sequence dashboard
Open Settings → Webhooks and create a new webhook. Paste your endpoint URL, choose the events you want, and save.

3. Trigger a test event

Create a customer or an invoice in the dashboard. Within a few seconds the request should appear at your endpoint. Confirm the payload’s notificationType matches the event you triggered.

Verify the signature

Every webhook arrives with a Sequence-Signature header containing a timestamp and HMAC signature:
Sequence-Signature: t=1748866120822,s=6be6887a470c20978427ff89941ebecb83b2804ceed66fd93b3afc00de2e9f11
To verify a delivery:
  1. Concatenate the timestamp and the raw request body with a . separator: TIMESTAMP.RAW_BODY. For example: 1779974397972.{"notificationType":"..."}
  2. Compute HMAC-SHA-256 of that string using your webhook’s signing secret
  3. Compare the result with s using a constant-time comparison
Use raw bytes, not re-serialised JSON. The signature is computed over the exact bytes Sequence sent. If you parse the body to a JSON object and re-stringify it before verification, whitespace and key ordering differences will produce a different hash and verification will always fail.

Full working example

Save the following as verify-webhooks-server.ts. It runs an HTTP server on port 8080 that verifies signatures on requests to POST /webhook. Dependencies: npm install express @types/express tsx Run with: npx tsx verify-webhooks-server.ts
#!/usr/bin/env tsx
const express = require("express");
const cryptoModule = require("crypto");

// Your webhook secret (from webhooks settings in the Sequence dashboard)
const WEBHOOK_SECRET = "YOUR_WEBHOOK_SECRET";
const PORT = 8080;

const app = express();

function verifyWebhookSignature(payloadBytes: Buffer, signatureHeader: string, secret: string): boolean {
  const payloadString = payloadBytes.toString("utf-8");

  const parts = signatureHeader.split(",");
  const timestampPart = parts.find((p: string) => p.startsWith("t="));
  const signaturePart = parts.find((p: string) => p.startsWith("s="));

  if (!timestampPart || !signaturePart) return false;

  const timestamp = timestampPart.substring(2);
  const signatureFromHeader = signaturePart.substring(2);

  const dataToSign = `${timestamp}.${payloadString}`;
  const expectedSignature = cryptoModule
    .createHmac("sha256", secret)
    .update(dataToSign, "utf8")
    .digest("hex");

  const signatureBuffer = Buffer.from(signatureFromHeader, "hex");
  const expectedBuffer = Buffer.from(expectedSignature, "hex");

  if (signatureBuffer.length !== expectedBuffer.length) return false;
  return cryptoModule.timingSafeEqual(signatureBuffer, expectedBuffer);
}

// Ensure webhook handler receives the raw bytes via express.raw()
app.use("/webhook", express.raw({ type: "application/json" }));

app.post("/webhook", (req: any, res: any) => {
  const payloadBytes: Buffer = req.body;
  const signatureHeader = req.headers["sequence-signature"] as string;

  if (!payloadBytes?.length || !signatureHeader) {
    return res.status(400).send("Bad Request");
  }

  if (verifyWebhookSignature(payloadBytes, signatureHeader, WEBHOOK_SECRET)) {
    console.log("Webhook verified.");
    return res.status(200).send("OK");
  } else {
    console.log("Verification failed.");
    return res.status(403).send("Verification failed");
  }
});

app.listen(PORT, "0.0.0.0", () => {
  console.log(`Webhook server listening on http://0.0.0.0:${PORT}/webhook`);
});

Verify manually

When you want to confirm an end-to-end setup without writing code, two tools together are enough:
  1. Point your webhooks at webhook.site URL and trigger an event in Sequence
  2. You should see the webhook event captured in webhook.site with the raw payload and the Sequence-Signature header
  3. Open metatoolhub.com’s HMAC verification tool
  4. Algorithm: select “HMAC-SHA-256”
  5. Message/Data: paste the timestamp value, then ., then the raw event payload
  6. Secret Key: paste your webhook’s signing secret
  7. HMAC to Verify: paste the s value from the sequence-signature header
A “Valid HMAC” message confirms your secret and the payload are correct.
A Sequence webhook received by webhook.site with its signature verified in metatoolhub.com's HMAC tool

Handle webhooks well

  • Return 200 quickly. Acknowledge the request and do any non-trivial work asynchronously (via a queue or background worker). A slow handler blocks the delivery and risks retries
  • Handle out-of-order events and out-of-date payloads. You can use webhooks as a signal that something has changed, then re-read the resource from the Sequence API to get its current state
  • Be idempotent. The same event may arrive more than once if a previous delivery failed
  • Ignore unknown event types and fields. Sequence may add new event types or fields in the future. Accept and ignore types your handler doesn’t recognise rather than throwing

Troubleshooting

Signature mismatch

Almost always caused by verifying against re-serialised JSON instead of the raw request body. Confirm your framework gives you the exact bytes Sequence sent. A mismatch in the working example above prints lines like:
Verification failed.
Details: Got='abc123...', Expected='def456...'
You can use an online HMAC verification tool like metatoolhub.com to verify the expected signature.

Webhook not received, or repeatedly retried

Your handler must return a 2xx response. Confirm the endpoint is publicly reachable and isn’t returning an error to Sequence. Any non-2xx response causes Sequence to retry. Failed deliveries are retried for up to 24 hours.

Next step

For the full event catalog, payload envelope, signature format, and delivery behaviour, see Webhook events.