Skip to content

Webhooks

Webhooks let you subscribe to form events and receive an HTTP POST to your endpoint each time one occurs. Use them to trigger workflows, sync data, or notify external systems in real time.

Webhooks are configured at the team level - navigate to your team dashboard and open Webhooks.

Click Add Webhook and fill in the following:

FieldRequiredDescription
NameNoA label to identify this webhook (e.g. “Zapier integration”)
Endpoint URLYesThe HTTPS URL that will receive POST requests
EventsYesOne or more events to subscribe to

After clicking Create, a secret is shown once. Copy it immediately - it is not stored in plaintext and cannot be retrieved again. You’ll use this secret to verify incoming requests.

EventWhen it fires
session_createdA user opens the form and begins a session
step_submittedA user submits a screen
form_completedA user reaches the end of the form flow

All events share a common set of top-level fields, with event-specific data added at the same level.

{
"event": "form_completed",
"timestamp": "2026-05-23T14:30:00Z",
"api_version": "2026-05",
"team_id": "abc123",
"form": {
"id": "def456",
"name": "Contact Form"
}
}
{
"event": "session_created",
"session": {
"id": "sess_abc",
"metadata": {},
"created_at": "2026-05-23T14:30:00Z"
}
}
{
"event": "step_submitted",
"session": {
"id": "sess_abc"
},
"step": {
"id": "step_xyz",
"step_name": "Contact Info",
"step_id": "screen-1",
"data": {
"full_name": "Jane Doe",
"email": "jane@example.com"
},
"submitted_at": "2026-05-23T14:30:15Z"
}
}
{
"event": "form_completed",
"session": {
"id": "sess_abc",
"metadata": {},
"created_at": "2026-05-23T14:30:00Z",
"completed_at": "2026-05-23T14:31:00Z"
},
"steps": [
{
"id": "step_xyz",
"step_name": "Contact Info",
"step_id": "screen-1",
"data": { "full_name": "Jane Doe" },
"submitted_at": "2026-05-23T14:30:15Z"
}
]
}

Every request includes three headers you should use to verify authenticity:

HeaderValue
X-FormRamp-EventThe event type (e.g. form_completed)
X-FormRamp-TimestampUnix timestamp of the delivery (seconds)
X-FormRamp-Signaturet={timestamp},v1={signature}
X-FormRamp-DeliveryUnique ID for this delivery attempt

To verify a request:

  1. Extract the timestamp from X-FormRamp-Signature (the t= component).
  2. Concatenate: "{timestamp}.{raw request body}".
  3. Compute HMAC-SHA256 of that string using your webhook secret as the key.
  4. Compare your result against the v1= component of the signature header.
  5. Check that the timestamp is within an acceptable window (e.g. 5 minutes) to prevent replay attacks.

Example in Node.js:

import crypto from "crypto";
function verifySignature(
secret: string,
body: string,
signatureHeader: string,
): boolean {
const parts = Object.fromEntries(
signatureHeader.split(",").map((p) => p.split("=")),
);
const timestamp = parts["t"];
const expected = crypto
.createHmac("sha256", secret)
.update(`${timestamp}.${body}`)
.digest("hex");
return crypto.timingSafeEqual(
Buffer.from(parts["v1"]),
Buffer.from(expected),
);
}

Always use a timing-safe comparison to avoid timing attacks.

FormRamp delivers webhooks asynchronously. If your endpoint returns a non-2xx status code or doesn’t respond within 10 seconds, the delivery is retried with exponential backoff - up to 5 attempts total.

After 3 consecutive failures (across all retries), the webhook is automatically disabled. You can re-enable it from the webhook detail page after resolving the issue.

Click any webhook in the list to open its detail page, which shows:

  • Health status and consecutive failure count
  • Last delivery time and status
  • A log of recent deliveries with response codes and response bodies

Use the delivery log to debug failed deliveries without needing to set up external tooling.