View .md

MuxForms webhooks POST JSON to your HTTPS URL when a submission is stored. Each form has one endpoint and a whsec signing secret; verify the MuxForms-Signature header with HMAC-SHA256. Failed deliveries retry with backoff and appear in the dashboard log.

Docs

Submission webhooks

Push every new submission into your API, queue, or automation. Choose Custom URL on the form Integrations tab.

Webhook delivery

ItemValue
Eventsubmission.created
MethodPOST
Signature headerMuxForms-Signature (t=, v1=)
Retries3 attempts (0s, 2s, 5s backoff)
ConfigureDashboard → form → Integrations tab (Custom URL)

Setup

Open a form in the dashboard, go to Webhooks, and save an HTTPS endpoint. MuxForms generates a signing secret (whsec_…) you verify on your server. Use http://localhost only for local testing.

Request

MuxForms sends POST with Content-Type: application/json and User-Agent: MuxForms-Webhooks/1. The body is UTF-8 JSON:

Payload

{
  "event": "submission.created",
  "form": { "id": "…", "name": "…", "project": "…" },
  "submission": {
    "id": "…",
    "createdAt": "…",
    "payload": { "email": "jane@example.com", "message": "Hello" },
    "origin": "https://yoursite.com",
    "ip": "…",
    "userAgent": "…"
  }
}

The MuxForms-Signature header is t=<unix>,v1=<hmac>. The HMAC is SHA-256 over {timestamp}.{rawBody} using your signing secret. Reject timestamps more than five minutes from the clock.

Verify (Node.js)

Node.js

// Verify MuxForms-Signature (Node.js)
const crypto = require("crypto");

function verifyMuxFormsWebhook(secret, rawBody, signatureHeader) {
  const parts = Object.fromEntries(
    signatureHeader.split(",").map((p) => p.trim().split("=")),
  );
  const timestamp = Number(parts.t);
  const expected = parts.v1;
  if (!timestamp || !expected) return false;
  if (Math.abs(Date.now() / 1000 - timestamp) > 300) return false;
  const actual = crypto
    .createHmac("sha256", secret)
    .update(`${timestamp}.${rawBody}`)
    .digest("hex");
  return actual === expected;
}

In Express, read the raw body before JSON parsing so the signature matches the bytes MuxForms sent.

Retries and delivery log

Non-2xx responses and network errors trigger up to three delivery attempts with backoff (immediate, then 2s, then 5s). Each run is recorded in the dashboard delivery log. Use Resend to replay a past submission payload to your current webhook URL.

Webhooks fire for submissions accepted through the embed and the submit API. Email notifications are independent—you can use both.

Common questions

Where do I get the signing secret?
It is shown in the Webhooks tab after you save a URL. Rotate it there if it leaks.
What HTTP status should my endpoint return?
Any 2xx response counts as success. Other codes trigger retries.
Can I replay an old submission?
Yes. Use Resend on a row in the delivery log to POST the same payload again.
Do webhooks replace email?
No. Email notifications and webhooks are independent per form.

Your endpoint, your stack.

Slack, CRM, n8n, or a custom worker—one POST per lead.

Upgrade to Pro