Skip to main content
Webhooks allow you to receive HTTP POST notifications when events occur in your database. Instead of polling for changes, you can subscribe to specific events and receive instant updates.

Event Types

Webhooks support the following event types:

Record Events

EventDescription
record.createdA new record was added to a table
record.updatedAn existing record was modified
record.deletedA record was deleted from a table

Table Events

EventDescription
table.createdA new table was added to the database
table.updatedA table’s properties (like name) were changed
table.deletedA table was deleted

Field Events

EventDescription
field.createdA new field was added to a table
field.updatedA field’s properties or configuration were changed
field.deletedA field was removed from a table

Webhook Payload

When an event occurs, we send an HTTP POST request to your webhook URL with a JSON payload:
{
  "id": "evt_a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "type": "record.created",
  "timestamp": "2025-01-15T10:30:00.000Z",
  "data": {
    "tableId": "tbl_abc123",
    "recordIds": ["rec_xyz789"],
    "records": [
      {
        "id": "rec_xyz789",
        "data": {
          "Name": "John Doe",
          "Email": "john@example.com"
        }
      }
    ]
  },
  "metadata": {
    "webhookId": 123,
    "organizationId": 456,
    "baseId": "base_def456",
    "attempt": 1,
    "source": "ui"
  }
}

Payload Fields

FieldTypeDescription
idstringUnique event ID for idempotency
typestringThe event type (e.g., record.created)
timestampstringISO 8601 timestamp of when the event occurred
dataobjectEvent-specific data (see below)
metadata.webhookIdnumberID of the webhook subscription
metadata.organizationIdnumberYour organization ID
metadata.baseIdstringThe database ID
metadata.attemptnumberDelivery attempt number (1 for first attempt)
metadata.sourcestringWhere the change originated (ui, public_api, zapier, make, import)

Record Event Data

For record events (record.created, record.updated, record.deleted):
{
  "tableId": "tbl_abc123",
  "recordIds": ["rec_xyz789"],
  "records": [...],
  "previousRecords": [...]  // Only for record.updated
}

Table Event Data

For table events (table.created, table.updated, table.deleted):
{
  "tableId": "tbl_abc123",
  "table": {
    "id": "tbl_abc123",
    "name": "Customers",
    "order": 0,
    "primaryFieldId": "fld_xyz"
  },
  "previousTable": {...}  // Only for table.updated
}

Field Event Data

For field events (field.created, field.updated, field.deleted):
{
  "tableId": "tbl_abc123",
  "fieldId": "fld_xyz789",
  "field": {
    "id": "fld_xyz789",
    "name": "Email",
    "type": "email",
    "order": 1
  },
  "previousField": {...}  // Only for field.updated
}

Verifying Webhook Signatures

Each webhook request includes an X-Webhook-Signature header containing an HMAC-SHA256 signature. Verify this signature to ensure the request came from Fillout.
const crypto = require('crypto');

function verifyWebhookSignature(payload, signature, secret) {
  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(JSON.stringify(payload))
    .digest('hex');

  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expectedSignature)
  );
}

// In your webhook handler
app.post('/webhooks/fillout', (req, res) => {
  const signature = req.headers['x-webhook-signature'];
  const isValid = verifyWebhookSignature(req.body, signature, YOUR_WEBHOOK_SECRET);

  if (!isValid) {
    return res.status(401).send('Invalid signature');
  }

  // Process the webhook...
  res.status(200).send('OK');
});

Request Headers

Each webhook request includes these headers:
HeaderDescription
Content-Typeapplication/json
X-Webhook-SignatureHMAC-SHA256 signature of the payload
X-Webhook-EventThe event type (e.g., record.created)
X-Webhook-IDThe unique event ID
User-AgentFillout-Webhooks/1.0

Retry Policy

If your webhook endpoint returns a non-2xx status code or times out, we’ll retry the delivery:
  • Maximum retries: 5
  • Retry delays: 5s, 10s, 20s, 40s, 80s (exponential backoff)
  • Timeout: 30 seconds per request
Your endpoint should return a 2xx status code within 30 seconds to acknowledge receipt. Process the webhook asynchronously if needed.

Limits

LimitValue
Max webhooks per database100
Max events per webhook20
Max URL length2,048 characters
Max payload size256 KB
Delivery timeout30 seconds

Best Practices

  1. Respond quickly: Return a 2xx status immediately, then process asynchronously
  2. Handle duplicates: Use the event id for idempotency in case of retries
  3. Verify signatures: Always verify the X-Webhook-Signature header
  4. Use HTTPS: Webhook URLs must use HTTPS for security
  5. Store your secret: Save the webhook secret securely - it’s only shown once on creation