Webhooks
Integrate form submissions with webhooks
Form Webhooks
Webhooks allow you to receive real-time notifications when form events occur, enabling integrations with external services.
Setting Up Webhooks
Via Dashboard
- Navigate to Forms > Select a form > Settings
- Scroll to Webhooks
- Enter your webhook URL
- Save changes
Via API
const response = await fetch('/api/forms/{formId}', {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
'X-API-Key': 'your-api-key',
},
body: JSON.stringify({
webhookUrl: 'https://your-server.com/webhook/forms',
}),
});Multiple Webhooks
For advanced integrations, configure multiple webhooks:
const response = await fetch('/api/forms/{formId}/webhooks', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': 'your-api-key',
},
body: JSON.stringify({
url: 'https://your-server.com/webhook/forms',
events: ['submission.created', 'submission.updated'],
secret: 'your-webhook-secret',
}),
});Webhook Events
submission.created
Triggered when a new form submission is completed.
{
"event": "submission.created",
"timestamp": "2024-01-15T10:30:00Z",
"form": {
"id": "form-uuid",
"title": "Contact Form",
"slug": "contact-form"
},
"submission": {
"id": "submission-uuid",
"data": {
"name": "John Doe",
"email": "john@example.com",
"message": "Hello from the form!"
},
"metadata": {
"sessionId": "session-uuid",
"ipAddress": "192.168.1.1",
"userAgent": "Mozilla/5.0...",
"referrer": "https://example.com/page",
"duration": 45,
"submittedAt": "2024-01-15T10:30:00Z"
}
}
}submission.updated
Triggered when a partial submission is updated.
{
"event": "submission.updated",
"timestamp": "2024-01-15T10:28:00Z",
"form": {
"id": "form-uuid",
"title": "Contact Form"
},
"submission": {
"id": "submission-uuid",
"data": {
"name": "John Doe",
"email": "john@example.com"
},
"status": "partial",
"currentField": 2
}
}form.view
Triggered when a form is viewed.
{
"event": "form.view",
"timestamp": "2024-01-15T10:25:00Z",
"form": {
"id": "form-uuid",
"title": "Contact Form"
},
"metadata": {
"sessionId": "session-uuid",
"referrer": "https://example.com/page"
}
}Webhook Security
Signature Verification
All webhooks are signed with HMAC-SHA256. Verify the signature to ensure authenticity:
import crypto from 'crypto';
function verifyWebhookSignature(
payload: string,
signature: string,
secret: string
): boolean {
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(`sha256=${expectedSignature}`)
);
}
// In your webhook handler
app.post('/webhook/forms', (req, res) => {
const signature = req.headers['x-transactional-signature'];
const payload = JSON.stringify(req.body);
if (!verifyWebhookSignature(payload, signature, process.env.WEBHOOK_SECRET)) {
return res.status(401).json({ error: 'Invalid signature' });
}
// Process webhook...
res.status(200).json({ received: true });
});IP Allowlist
Webhook requests come from these IPs:
52.xxx.xxx.xxx
52.xxx.xxx.xxx
You can allowlist these IPs for additional security.
Webhook Headers
Every webhook request includes these headers:
| Header | Description |
|---|---|
Content-Type | application/json |
X-Transactional-Event | Event type (e.g., submission.created) |
X-Transactional-Signature | HMAC signature |
X-Transactional-Timestamp | Request timestamp |
X-Transactional-Delivery-Id | Unique delivery ID |
Retry Policy
Failed webhooks are retried with exponential backoff:
| Attempt | Delay |
|---|---|
| 1 | Immediate |
| 2 | 1 minute |
| 3 | 5 minutes |
| 4 | 30 minutes |
| 5 | 2 hours |
| 6 | 12 hours |
After 6 failed attempts, the webhook is marked as failed and no further retries occur.
Success Criteria
A webhook is considered successful if:
- HTTP status code is 2xx
- Response is received within 30 seconds
Webhook Logs
View webhook delivery history in the dashboard:
- Navigate to Forms > Select a form > Webhooks
- Click on a webhook to view logs
Each log entry shows:
- Delivery timestamp
- Event type
- HTTP status code
- Response body
- Retry count
API Access
// Get webhook delivery logs
const response = await fetch('/api/forms/{formId}/webhooks/{webhookId}/logs', {
headers: {
'X-API-Key': 'your-api-key',
},
});
const { data } = await response.json();
// Array of delivery logsIntegration Examples
Slack Notification
app.post('/webhook/forms', async (req, res) => {
const { event, form, submission } = req.body;
if (event === 'submission.created') {
await fetch(process.env.SLACK_WEBHOOK_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
text: `New submission on ${form.title}`,
blocks: [
{
type: 'section',
text: {
type: 'mrkdwn',
text: `*New Form Submission*\n*Form:* ${form.title}`,
},
},
{
type: 'section',
fields: Object.entries(submission.data).map(([key, value]) => ({
type: 'mrkdwn',
text: `*${key}:*\n${value}`,
})),
},
],
}),
});
}
res.status(200).json({ received: true });
});Google Sheets
import { google } from 'googleapis';
app.post('/webhook/forms', async (req, res) => {
const { event, submission } = req.body;
if (event === 'submission.created') {
const sheets = google.sheets({ version: 'v4', auth: authClient });
await sheets.spreadsheets.values.append({
spreadsheetId: process.env.SHEET_ID,
range: 'Sheet1!A:Z',
valueInputOption: 'RAW',
requestBody: {
values: [[
new Date().toISOString(),
submission.data.name,
submission.data.email,
submission.data.message,
]],
},
});
}
res.status(200).json({ received: true });
});Zapier
Create a Zapier webhook and use it as your form's webhook URL:
- Create a new Zap
- Choose "Webhooks by Zapier" as trigger
- Select "Catch Hook"
- Copy the webhook URL
- Add it to your form settings
HubSpot CRM
app.post('/webhook/forms', async (req, res) => {
const { event, submission } = req.body;
if (event === 'submission.created') {
await fetch('https://api.hubspot.com/crm/v3/objects/contacts', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.HUBSPOT_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
properties: {
email: submission.data.email,
firstname: submission.data.name?.split(' ')[0],
lastname: submission.data.name?.split(' ').slice(1).join(' '),
phone: submission.data.phone,
company: submission.data.company,
},
}),
});
}
res.status(200).json({ received: true });
});Custom CRM
app.post('/webhook/forms', async (req, res) => {
const { event, form, submission } = req.body;
if (event === 'submission.created') {
// Map form fields to your CRM fields
const lead = {
source: 'Website Form',
formName: form.title,
name: submission.data.name,
email: submission.data.email,
phone: submission.data.phone,
message: submission.data.message,
createdAt: submission.metadata.submittedAt,
};
await yourCRM.leads.create(lead);
}
res.status(200).json({ received: true });
});Testing Webhooks
Test Endpoint
Send a test webhook to verify your endpoint:
const response = await fetch('/api/forms/{formId}/webhooks/{webhookId}/test', {
method: 'POST',
headers: {
'X-API-Key': 'your-api-key',
},
});Local Development
Use ngrok or similar to test webhooks locally:
ngrok http 3000Then use the ngrok URL as your webhook URL.
Troubleshooting
Webhook Not Triggering
- Check form is published
- Verify webhook URL is correct
- Check webhook logs for errors
- Ensure server returns 2xx status
Signature Verification Failing
- Use the raw request body for verification
- Check secret matches
- Ensure no body parsing middleware modifies the payload
Timeouts
- Webhooks timeout after 30 seconds
- Return 200 immediately, process async
- Use a queue for long-running tasks
app.post('/webhook/forms', async (req, res) => {
// Acknowledge immediately
res.status(200).json({ received: true });
// Process in background
processSubmission(req.body).catch(console.error);
});Next Steps
- API Reference - Full API documentation
- JavaScript SDK - Client-side SDK
- Submissions - Managing submissions
On This Page
- Setting Up Webhooks
- Via Dashboard
- Via API
- Multiple Webhooks
- Webhook Events
- submission.created
- submission.updated
- form.view
- Webhook Security
- Signature Verification
- IP Allowlist
- Webhook Headers
- Retry Policy
- Success Criteria
- Webhook Logs
- API Access
- Integration Examples
- Slack Notification
- Google Sheets
- Zapier
- HubSpot CRM
- Custom CRM
- Testing Webhooks
- Test Endpoint
- Local Development
- Troubleshooting
- Webhook Not Triggering
- Signature Verification Failing
- Timeouts
- Next Steps