Webhooks

Webhooks push events to your own endpoints the moment they happen — a visitor qualifies, a lead is assigned, a score changes. Use them to trigger workflows in internal tools, data warehouses or any system Aurora doesn't integrate with natively.

5 min read Updated Aug 22, 2026
On this page
  1. Create an endpoint
  2. Event types
  3. Payload format
  4. Verifying signatures
  5. Retries and delivery
  6. Ordering and idempotency
  7. Testing locally
  8. Security checklist

Create an endpoint#

  1. Add the URLGo to Settings → Webhooks and choose Add endpoint. Enter a publicly reachable https:// URL; plain HTTP is not accepted.
  2. Choose eventsSelect the event types this endpoint should receive. Subscribing only to what you need keeps your handler simple and your logs quiet.
  3. Copy the signing secretEach endpoint gets its own secret, starting with whsec_. Store it in your secret manager; you'll use it to verify every request.
  4. Send a test eventChoose Send test to deliver a sample payload and see your endpoint's response code and body in the delivery log.

Endpoints can also be managed programmatically with the Webhooks API.

Event types#

EventSent when
visitor.qualifiedA visitor's score crosses the threshold for the first time
visitor.score_changedA score changes by 10 points or more (configurable per endpoint)
visitor.identifiedAn anonymous visitor is linked to an email address
visitor.mergedTwo visitor profiles are merged into one
lead.assignedA routing rule assigns an owner, or an owner is changed
lead.status_changedA lead moves between statuses, such as ready → contacted
rule.publishedA scoring or routing rule is published or restored

Payload format#

Every event is sent as an HTTP POST with a JSON body and the same envelope: a unique id, the event type, a created_at timestamp and a data object whose shape depends on the type.

visitor.qualified
{
  "id": "evt_01J8Z6Q3T9M2",
  "type": "visitor.qualified",
  "created_at": "2026-08-24T09:41:12Z",
  "workspace_id": "wsp_4Hk29",
  "data": {
    "visitor": {
      "id": "vis_7c1e02",
      "email": "dana@northwind.example",
      "name": "Dana Whitfield",
      "score": 94,
      "status": "ready",
      "account": { "name": "Northwind Supply", "domain": "northwind.example" },
      "top_signals": [
        { "rule": "Pricing page", "points": 36 },
        { "rule": "Came back", "points": 18 },
        { "rule": "Integration docs", "points": 11 }
      ],
      "profile_url": "https://app.aurora.io/v/vis_7c1e02"
    },
    "threshold": 60
  }
}
HeaderDescription
Aurora-SignatureTimestamp and HMAC signature, used to verify the request
Aurora-Event-IdSame as id in the body; use it for idempotency
Aurora-Delivery-Attempt1 for the first attempt, incremented on each retry
User-AgentAurora-Webhooks/2.0

Verifying signatures#

Always verify that a request came from Aurora before acting on it. The Aurora-Signature header looks like t=1724492472,v1=5f2b…. To verify it:

  1. Split the header into the timestamp t and the signature v1.
  2. Build the signed string: the timestamp, a period, and the raw request body.
  3. Compute an HMAC-SHA256 of that string with your endpoint secret, hex-encoded.
  4. Compare it to v1 with a constant-time comparison, and reject timestamps older than five minutes.
Node.js · Express
import crypto from "node:crypto";
import express from "express";

const app = express();
const SECRET = process.env.AURORA_WEBHOOK_SECRET; // whsec_...

// Use the raw body: re-serialized JSON will not match the signature.
app.post("/webhooks/aurora", express.raw({ type: "application/json" }), (req, res) => {
  const header = req.get("Aurora-Signature") || "";
  const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
  const signed = `${parts.t}.${req.body.toString("utf8")}`;
  const expected = crypto.createHmac("sha256", SECRET).update(signed).digest("hex");

  const valid =
    parts.v1 &&
    crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1)) &&
    Math.abs(Date.now() / 1000 - Number(parts.t)) < 300; // reject replays older than 5 min

  if (!valid) return res.status(400).send("Invalid signature");

  const event = JSON.parse(req.body.toString("utf8"));
  // Acknowledge fast, then process asynchronously (queue, background job, etc.).
  res.sendStatus(200);
  handleEvent(event);
});

Parse the JSON only after verifying the signature, and compute the signature over the exact bytes you received. Most verification failures come from frameworks that parse and re-serialize the body first.

Retries and delivery#

Aurora considers a delivery successful when your endpoint returns any 2xx status within 10 seconds. Anything else — a timeout, a connection error, a 4xx or 5xx — is retried with exponential backoff:

AttemptDelay after previous attempt
21 minute
35 minutes
430 minutes
52 hours
66 hours
7–812 hours

After eight failed attempts over roughly 32 hours, the event is marked failed and can be redelivered manually from the delivery log for up to 30 days. If an endpoint fails every delivery for three consecutive days, it's disabled automatically and workspace admins are notified by email.

Ordering and idempotency#

Events are delivered at least once and usually in order, but neither duplicates nor out-of-order delivery can be ruled out — especially during retries. Design your handler so that:

  • Processing the same Aurora-Event-Id twice has no additional effect. Store processed IDs for at least seven days.
  • Stale updates don't overwrite newer ones. Compare created_at with the last event you applied for the same visitor.
  • The handler returns 200 quickly and does slow work (API calls, database writes) in a background job.

Testing locally#

To receive webhooks on your laptop, expose a local port with a tunneling tool such as ngrok or cloudflared and register the tunnel URL as a separate endpoint. Keep test endpoints subscribed only to the events you're working on, and delete them when you're done. Every delivery, including the request body and your response, is visible in Settings → Webhooks → Delivery log for 30 days.

Security checklist#

  • Verify every signature and reject old timestamps.
  • Use a separate secret per endpoint and rotate it from the endpoint page if it's ever exposed. Old secrets keep working for 24 hours after rotation so you can deploy without downtime.
  • If you allowlist inbound IPs, use the published ranges at https://aurora.io/ips.json, which differ for EU and US regions.
  • Never log full payloads in systems that don't need personal data.
Last updated Aug 22, 2026 Report an issue with this page

Ready to try it on your own site?

Install the snippet in five minutes and see your first scored visitors today. Starter is free forever for one seat.

No credit card required · Setup help from real engineers