Skip to content

Developers

Webhooks

Get notified in real time, verify signatures and handle retries.

Webhooks (Pro) send an HTTPS POST to your endpoint when something happens.

Events

EventWhen
response.createdA response was submitted (web, embed or API)
response.approval_changedAn owner overrode or reset an approval decision
response.deletedA response was deleted
pingYou clicked Send test

Payload

{
  "id": "6650d0e9…",
  "type": "response.created",
  "created_at": "2026-10-18T09:24:12Z",
  "data": {
    "form": { "id": "6650…", "slug": "k3x9m2pq7a", "title": "Job application" },
    "response": {
      "id": "6650c1f2…",
      "form_version": 3,
      "created_at": "2026-10-18T09:24:11.502Z",
      "source": "web",
      "answers": { "email": "ada@example.com" },
      "approval": { "status": "approved", "score": 5, "max_score": 6, "percent": 83.33 },
      "url": "https://your-domain/dashboard/forms/6650…/responses/6650c1f2…"
    }
  }
}

Headers: X-CreateAForm-Event, X-CreateAForm-Delivery (unique id, use it to de-duplicate) and X-CreateAForm-Signature.

Verify the signature

The signature header looks like t=1729243452,v1=5257a869…. Compute HMAC-SHA256(secret, "{t}.{raw body}") in hex and compare it in constant time; reject requests older than 5 minutes.

import hmac, hashlib, time

def verify(secret: str, header: str, body: bytes) -> bool:
    parts = dict(p.split("=", 1) for p in header.split(","))
    if abs(time.time() - int(parts["t"])) > 300:
        return False
    expected = hmac.new(secret.encode(), f"{parts['t']}.".encode() + body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, parts["v1"])

Always verify against the raw request body, before parsing JSON.

Retries

Respond with any 2xx within 10 seconds. Otherwise the delivery is retried up to 8 times with increasing delays (30 s, 2 min, 10 min, 30 min, 1 h, 3 h, 6 h, 12 h). After 25 consecutive failures the endpoint is disabled; re-enable it in the dashboard.

Deliveries may arrive more than once or out of order — use X-CreateAForm-Delivery and created_at.

Security rules

  • Endpoints must use https:// and resolve to a public IP address (private, loopback and cloud metadata addresses are refused, even via DNS tricks).
  • Redirects are not followed.
  • Only signed-in users can manage webhooks; API keys cannot.