AutomationLive

Webhooks

The universal escape hatch. POST a signed JSON payload of every submission to your own backend or any automation platform. Verify authenticity with the HMAC signature, see every delivery in the log.

  • HMAC-SHA256 signed payloads (x-askery-signature)
  • Automatic retries with backoff + a delivery log
  • Per-form or workspace-wide endpoints

Set it up

  1. 1

    Add an endpoint

    Integrations → Webhooks → Add and paste your https:// URL (or use a form's Integrations tab). We generate a signing secret automatically.

  2. 2

    Verify the signature

    On your server, recompute HMAC-SHA256 of the raw body with the endpoint's secret and compare it to the x-askery-signature header before trusting the payload.

  3. 3

    Handle the payload

    Each POST includes the event, the form (id + title) and the response answers. Respond 2xx to acknowledge — non-2xx triggers our retry-with-backoff.

Where to configure: Integrations hub (workspace) · builder Integrations tab (per form)

Verify the signature

Before trusting any incoming payload, recompute HMAC-SHA256(rawBody, secret) and compare to x-askery-signature in constant time. Use the raw request body — JSON-parsing and re-stringifying changes byte order and breaks the comparison. Both snippets handle that correctly.

// Express / Next.js Route Handler — verify x-askery-signature
import { createHmac, timingSafeEqual } from "node:crypto";

const SECRET = process.env.ASKERY_WEBHOOK_SECRET!;  // whsec_…

export async function POST(req: Request) {
  // CRITICAL: use the RAW body. Don't JSON.parse + re-stringify — the
  // bytes won't match what we signed.
  const raw = await req.text();
  const header = req.headers.get("x-askery-signature") ?? "";
  const provided = header.replace(/^sha256=/, "");

  const expected = createHmac("sha256", SECRET).update(raw).digest("hex");

  const a = Buffer.from(expected, "hex");
  const b = Buffer.from(provided, "hex");
  if (a.length !== b.length || !timingSafeEqual(a, b)) {
    return new Response("invalid signature", { status: 401 });
  }

  const event = req.headers.get("x-askery-event"); // always "response.submitted"
  const payload = JSON.parse(raw);
  // … your business logic …
  return new Response("ok");
}

Need the full payload reference (every field, FI-on vs FI-off)? Open the developers page in your dashboard for the interactive payload explorer + delivery log.

Ready to build?

Create a form and connect Webhooks in minutes.

More automation