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.
Creating a webhook
Section titled “Creating a webhook”Click Add Webhook and fill in the following:
| Field | Required | Description |
|---|---|---|
| Name | No | A label to identify this webhook (e.g. “Zapier integration”) |
| Endpoint URL | Yes | The HTTPS URL that will receive POST requests |
| Events | Yes | One 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.
Events
Section titled “Events”| Event | When it fires |
|---|---|
session_created | A user opens the form and begins a session |
step_submitted | A user submits a screen |
form_completed | A user reaches the end of the form flow |
Payload
Section titled “Payload”All events share a common set of top-level fields, with event-specific data added at the same level.
Common fields
Section titled “Common fields”{ "event": "form_completed", "timestamp": "2026-05-23T14:30:00Z", "api_version": "2026-05", "team_id": "abc123", "form": { "id": "def456", "name": "Contact Form" }}session_created
Section titled “session_created”{ "event": "session_created", "session": { "id": "sess_abc", "metadata": {}, "created_at": "2026-05-23T14:30:00Z" }}step_submitted
Section titled “step_submitted”{ "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" }}form_completed
Section titled “form_completed”{ "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" } ]}Verifying signatures
Section titled “Verifying signatures”Every request includes three headers you should use to verify authenticity:
| Header | Value |
|---|---|
X-FormRamp-Event | The event type (e.g. form_completed) |
X-FormRamp-Timestamp | Unix timestamp of the delivery (seconds) |
X-FormRamp-Signature | t={timestamp},v1={signature} |
X-FormRamp-Delivery | Unique ID for this delivery attempt |
To verify a request:
- Extract the timestamp from
X-FormRamp-Signature(thet=component). - Concatenate:
"{timestamp}.{raw request body}". - Compute HMAC-SHA256 of that string using your webhook secret as the key.
- Compare your result against the
v1=component of the signature header. - 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.
Delivery and retries
Section titled “Delivery and retries”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.
Delivery history
Section titled “Delivery history”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.