Webhooks
Forward submissions to your own server or third-party services in real time.
Overview
Webhooks let Submito push submission data to any URL you control as soon as a submission arrives. Use webhooks to integrate with CRMs, trigger automation workflows, sync data to your own database, or connect to platforms like Zapier or Make.
Creating a Webhook
- Go to Workspace Settings > Connected Apps.
- Click Add Connection and select Webhook.
- Enter a name and your endpoint URL (must be HTTPS).
- Optionally add custom headers (e.g. an authorization token for your server).
- Click Save.
Once created, the webhook is active for all forms in the workspace. You can enable or disable it per form in Form Settings > Integrations, or toggle the entire connection from Workspace Settings.
The number of active webhook connections depends on your plan. Check Workspace Settings > Connected Apps for your current limit.
Webhook Payload
Submito sends a POST request with a JSON body to your endpoint:
{
"event": "submission.created",
"submission_id": "sub_a1b2c3d4",
"form_id": "form_xyz",
"form_name": "Contact Form",
"workspace_id": "ws_abc123",
"submitted_at": "2025-03-15T14:32:00Z",
"ip_address": "203.0.113.42",
"referrer": "https://yoursite.com/contact",
"is_spam": false,
"data": {
"name": "Jane Doe",
"email": "[email protected]",
"message": "Hello, I'd like to discuss a project..."
}
}Signature Verification
Every webhook request includes an X-Submito-Signature header. This is an HMAC-SHA256 signature of the raw request body, computed using your webhook's secret key. Always verify this signature to ensure requests are genuinely from Submito.
Find your webhook secret in Workspace Settings > Connected Apps by clicking the connection name.
const crypto = require('crypto');
function verifySignature(rawBody, signature, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(rawBody)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(signature)
);
}
// Express example
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
const sig = req.headers['x-submito-signature'];
if (!verifySignature(req.body, sig, process.env.WEBHOOK_SECRET)) {
return res.status(401).send('Invalid signature');
}
const payload = JSON.parse(req.body);
// Handle payload...
res.status(200).send('OK');
});Always verify signatures
Without signature verification, anyone can send fake webhook payloads to your endpoint. Use hmac.compare_digest or equivalent constant-time comparison to prevent timing attacks.
Custom Headers
Add custom HTTP headers to your webhook requests - useful for authenticating with your own server. Common patterns:
# API key authentication
Authorization: Bearer your-server-api-key
# Custom secret header
X-My-Secret: your-custom-valueRetry Logic
If your endpoint returns a non-2xx response or times out, Submito automatically retries the delivery with exponential backoff:
| Attempt | Delay |
|---|---|
| 1st retry | 1 minute |
| 2nd retry | 5 minutes |
| 3rd retry | 30 minutes |
| 4th retry | 2 hours |
| 5th retry (final) | 6 hours |
After 5 failed attempts, the delivery is marked as failed. The submission data is never lost - it remains in Submito regardless of webhook delivery status.
Delivery Status
View delivery history for any webhook connection in Workspace Settings > Connected Apps. Each delivery record shows:
- Delivery timestamp
- HTTP status code received
- Number of attempts
- Response body (for debugging failed deliveries)
Testing Webhooks Locally
To test webhooks during development, expose your local server using a tunneling tool:
# Using ngrok
ngrok http 3000
# Using cloudflared
cloudflare tunnel --url http://localhost:3000Use the generated public URL as your webhook endpoint in Workspace Settings. Submit a test form entry to trigger a delivery to your local server.
