All articles Integration

Acting on Smart Signals via Webhooks

A device-intelligence API answers the question you ask at the moment you ask it. But some of the most valuable signals are not available at that instant. A visitor’s reputation can worsen after their first request, a device can be linked to a fraud ring hours later, and a sealed result can be verified server-side well after the page loaded. Webhooks are how those signals reach you without constant polling.

An event-driven integration turns device intelligence from a one-shot lookup into a live feed. When a signal changes, the platform posts it to your endpoint, and your handler decides what to do. This article covers how to subscribe, how to verify payloads so you can trust them, and how to design handlers that behave correctly under retries and load.

When webhooks beat synchronous calls

Synchronous API calls and webhooks are complements, not competitors. Each fits a different timing model.

  • Synchronous calls are right at a decision point: a login, a checkout, a signup. You need an answer now, and you block on it. See node server-side verification.
  • Webhooks are right for signals that arrive on their own schedule: a reputation downgrade, a newly detected device-farm link, a delayed bot classification, or an async challenge result.

The pattern that works well is to make the synchronous call for the immediate decision, then subscribe to webhooks for everything that develops afterward. A user who passed a clean login can still be flagged an hour later when their device turns up in a fraud ring, and the webhook is how you learn about it in time to freeze the session.

Subscribing to Smart Signals

You register an endpoint and select which events you care about. Subscribing to everything creates noise; subscribe to the signals your policy actually acts on.

EventFires whenTypical action
visitor.suspiciousSuspect score crosses a thresholdStep up or review
signal.bot_detectedA session is classified as automatedRate-limit, block
reputation.downgradedA device’s reputation worsensRe-evaluate open sessions
device.ring_linkedA device joins a known fraud clusterFreeze linked accounts
challenge.completedA proof-of-work challenge resolvesGrant or deny access

Scope your subscription to the events tied to a concrete response. Every event you subscribe to is code you have to maintain and a decision you have to define. For the signal catalog behind these events, see the bot detection and account takeover pillars, and suspect score for how the score that drives visitor.suspicious is computed.

Verifying the payload

A webhook endpoint is a public URL, which means anyone can POST to it. You must verify that a payload genuinely came from the platform before acting on it. The standard mechanism is an HMAC signature over the raw request body.

import crypto from "node:crypto";

function verify(req, secret) {
  const signature = req.headers["x-prynt-signature"];
  const expected = crypto
    .createHmac("sha256", secret)
    .update(req.rawBody)          // raw bytes, not parsed JSON
    .digest("hex");
  // constant-time compare to avoid timing leaks
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}

app.post("/webhooks/prynt", (req, res) => {
  if (!verify(req, process.env.PRYNT_WEBHOOK_SECRET)) {
    return res.status(401).end();
  }
  enqueue(req.body);   // hand off, then acknowledge fast
  res.status(200).end();
});

Three details matter. Compute the HMAC over the raw body, because reserializing parsed JSON changes bytes and breaks the signature. Use a constant-time comparison so an attacker cannot brute-force the signature through timing. And check a timestamp in the payload to reject replays of old, captured deliveries. The same discipline applies whether you are verifying webhooks or sealed results.

Designing handlers that survive reality

Networks fail, endpoints restart, and senders retry. A handler that assumes exactly-once delivery will eventually double-charge, double-ban, or double-notify. Build for at-least-once delivery instead.

  1. Acknowledge fast, process async. Verify the signature, enqueue the event, and return 200 immediately. Do the real work in a background worker so a slow downstream does not trigger sender retries.
  2. Be idempotent. Every event carries a unique ID. Record processed IDs and skip duplicates, so a retried delivery is a no-op.
  3. Order-independence. Events can arrive out of order. Key your logic on the event’s own timestamp and current state, not on arrival order.
  4. Fail safe. If enrichment data is missing, choose a default that does not silently allow fraud. A dropped signal.bot_detected should not quietly grant access.
async function handle(event) {
  if (await seen(event.id)) return;      // idempotency guard
  switch (event.type) {
    case "signal.bot_detected":
      await rateLimit(event.data.visitorId);
      break;
    case "device.ring_linked":
      await freezeAccounts(event.data.linkedAccounts);
      break;
  }
  await markSeen(event.id);
}

This structure lets you wire signals directly into enforcement without fragile assumptions. It pairs naturally with rate limiting by device and with reducing false positives, since an idempotent, state-aware handler will not compound a mistaken action.

Closing the loop

Webhooks are only valuable if the action they trigger is observable and reversible. Log every event, the decision it produced, and the outcome, so you can audit and tune. When a webhook freezes an account, make sure there is a path to unfreeze it, ideally driven by reason codes that tell support exactly why the action fired. An event-driven fraud system that cannot explain or reverse itself becomes a liability the first time it acts on a false positive. For the metrics that tell you whether your handlers are helping, see bot detection metrics and KPIs.

Frequently asked questions

Why use webhooks instead of just calling the API?

The API answers questions you ask synchronously at decision time. Webhooks push signals to you as they change, including asynchronous ones like reputation updates that arrive after the initial request, so you can react without polling.

How do I know a webhook really came from Prynt?

Every payload is signed with a shared secret. You recompute the HMAC over the raw body and compare it to the signature header using a constant-time check. Reject anything that does not match.

What happens if my webhook endpoint is down?

A well-designed sender retries with backoff. Your job is to make handlers idempotent so retried deliveries do not double-process, and to acknowledge quickly so the sender does not treat a slow response as a failure.

Webhooks turn device intelligence into a nervous system: signals fire as they develop, and your handlers respond in real time. Get the verification and idempotency right and you can automate enforcement with confidence. Get them wrong and you have a public endpoint acting on unverified input. Start with the docs for the event schema and signature format, and the SDKs for handler examples in your stack.

Run it yourself

Prynt is open-source, self-hostable device intelligence — visitor IDs, bot & fraud Smart Signals, and behavioral biometrics you own end to end.

Keep reading