Fodo Docs
Forms

Webhooks

Send form data to external APIs and services

Webhooks

This is a Pro feature. Upgrade to Pro to use webhooks.

Webhooks send form submission data to any URL endpoint in real-time. Build custom integrations, sync with databases, trigger automations, and more.

How webhooks work

  1. User submits form
  2. Form data is saved locally
  3. HTTP POST request sent to your URL
  4. Your server processes the data
  5. Response logged for debugging

Setting up webhooks

Per-form configuration

  1. Edit your form
  2. Go to Settings → Integrations → Webhooks
  3. Enable webhooks
  4. Enter your endpoint URL
  5. (Optional) Add secret for verification
  6. Save

Endpoint URL

Your server endpoint that receives the data:

https://api.yoursite.com/webhooks/forms
https://yoursite.com/wp-json/custom/v1/form-handler
https://hooks.zapier.com/hooks/catch/123456/abcdef/

Must be HTTPS in production.

Payload format

Request

POST /your-endpoint HTTP/1.1
Host: api.yoursite.com
Content-Type: application/json
X-Fodo-Event: form_submission
X-Fodo-Signature: sha256=abc123def456...

{
  "event": "form_submission",
  "form_id": 123,
  "form_title": "Contact Form",
  "entry_id": 456,
  "submitted_at": "2024-01-15T14:30:00Z",
  "fields": {
    "name": "John Smith",
    "email": "john@example.com",
    "phone": "+1-555-123-4567",
    "message": "I'm interested in your services..."
  }
}

Headers

HeaderDescription
Content-TypeAlways application/json
X-Fodo-EventEvent type (form_submission)
X-Fodo-SignatureHMAC signature for verification
User-AgentFodo-Forms/1.0

Field data

Fields are sent as key-value pairs:

{
  "fields": {
    "text_field": "Plain text value",
    "email_field": "user@example.com",
    "select_field": "selected_value",
    "checkbox_field": ["option1", "option2"],
    "file_field": {
      "name": "document.pdf",
      "url": "https://example.com/uploads/doc.pdf",
      "size": 245678,
      "type": "application/pdf"
    }
  }
}

Security

Signature verification

Every request includes an HMAC-SHA256 signature. Verify to ensure the request came from Fodo Forms.

PHP Example:

<?php
$payload = file_get_contents('php://input');
$secret = 'your_webhook_secret';

$expected = 'sha256=' . hash_hmac('sha256', $payload, $secret);
$received = $_SERVER['HTTP_X_FODO_SIGNATURE'] ?? '';

if (!hash_equals($expected, $received)) {
    http_response_code(401);
    exit('Invalid signature');
}

$data = json_decode($payload, true);
// Process $data...

Node.js Example:

const crypto = require('crypto');

app.post('/webhook', (req, res) => {
  const payload = JSON.stringify(req.body);
  const secret = process.env.WEBHOOK_SECRET;

  const expected = 'sha256=' +
    crypto.createHmac('sha256', secret)
          .update(payload)
          .digest('hex');

  const received = req.headers['x-fodo-signature'];

  if (!crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(received)
  )) {
    return res.status(401).send('Invalid signature');
  }

  // Process req.body...
  res.status(200).send('OK');
});

IP allowlisting

For additional security, allowlist Fodo Forms IP addresses. Contact support for current IPs.

Response handling

Expected response

Return a 2xx status code to indicate success:

HTTP/1.1 200 OK
Content-Type: application/json

{"status": "received"}

Failure response

Non-2xx responses trigger retry logic:

HTTP/1.1 500 Internal Server Error
Content-Type: application/json

{"error": "Database unavailable"}

Retry logic

Failed webhook deliveries are retried automatically:

AttemptDelayTotal Time
1st retry5 minutes5 min
2nd retry30 minutes35 min
3rd retry2 hours2 hr 35 min
Final6 hours8 hr 35 min

After all retries fail, the webhook is marked as failed and logged.

Debugging

Webhook logs

View delivery attempts in Fodo Forms → Logs → Webhooks:

[2024-01-15 14:30:05] POST https://api.example.com/webhook
  Status: 200 OK
  Duration: 245ms

[2024-01-15 14:32:10] POST https://api.example.com/webhook
  Status: 500 Error
  Response: {"error": "Database timeout"}
  Retry scheduled: 5 minutes

Test webhook

Send a test payload without submitting a form:

  1. Go to form webhook settings
  2. Click Send Test
  3. Check your endpoint received the data
  4. Review the response

Request inspection

Use services to inspect webhook payloads:

Use cases

Sync to database

app.post('/webhook', async (req, res) => {
  const { fields, entry_id } = req.body;

  await db.insert('leads', {
    entry_id,
    name: fields.name,
    email: fields.email,
    created_at: new Date()
  });

  res.json({ status: 'saved' });
});

Send to Slack

app.post('/webhook', async (req, res) => {
  const { fields, form_title } = req.body;

  await fetch(process.env.SLACK_WEBHOOK, {
    method: 'POST',
    body: JSON.stringify({
      text: `New submission: ${form_title}`,
      blocks: [
        { type: 'section', text: { type: 'mrkdwn',
          text: `*${fields.name}*\n${fields.email}\n\n${fields.message}`
        }}
      ]
    })
  });

  res.json({ status: 'sent' });
});

Create support ticket

app.post('/webhook', async (req, res) => {
  const { fields } = req.body;

  const ticket = await zendesk.createTicket({
    subject: fields.subject,
    requester: { email: fields.email, name: fields.name },
    comment: { body: fields.message }
  });

  res.json({ ticket_id: ticket.id });
});