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

  1. Go to Workspace Settings > Connected Apps.
  2. Click Add Connection and select Webhook.
  3. Enter a name and your endpoint URL (must be HTTPS).
  4. Optionally add custom headers (e.g. an authorization token for your server).
  5. 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-value

Retry Logic

If your endpoint returns a non-2xx response or times out, Submito automatically retries the delivery with exponential backoff:

AttemptDelay
1st retry1 minute
2nd retry5 minutes
3rd retry30 minutes
4th retry2 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:3000

Use the generated public URL as your webhook endpoint in Workspace Settings. Submit a test form entry to trigger a delivery to your local server.