Fly Super User Guide
Forms

Webhooks

Integrate forms with external services using webhooks for real-time data delivery

Webhooks

Webhooks let you send form submissions to external services in real-time. Connect to CRMs, marketing tools, databases, Zapier, or your own API endpoints with automatic retry logic for guaranteed delivery.

What Are Webhooks?

A webhook is an HTTP POST request sent to a URL of your choice when a form is submitted. The request contains the form submission data as JSON.

Use cases:

  • Send leads to your CRM (Salesforce, HubSpot, Pipedrive)
  • Add subscribers to email marketing tools (Mailchimp, ConvertKit)
  • Save data to databases (Airtable, Google Sheets, Notion)
  • Trigger automation workflows (Zapier, Make, n8n)
  • Notify your team (Slack, Discord, Microsoft Teams)
  • Custom processing in your own application

Quick Start

Step 1: Get Your Webhook URL

Get a webhook URL from your service:

Zapier:

  1. Create a Zap with "Webhooks by Zapier" trigger
  2. Choose "Catch Hook"
  3. Copy the webhook URL

Make (formerly Integromat):

  1. Create a scenario with "Webhooks" module
  2. Choose "Custom webhook"
  3. Copy the webhook URL

Airtable: Use Zapier/Make as a bridge, or create an Airtable script

Your Own API: Create an endpoint that accepts POST requests:

https://yourdomain.com/api/form-submissions

Step 2: Configure Webhook in Fly Super

  1. Navigate to Forms → Select your form
  2. Click "Webhooks" tab
  3. Enter your webhook URL
  4. (Optional) Add webhook secret for security
  5. Click "Save"

Step 3: Test Webhook

  1. Click "Test Webhook" button
  2. Fly Super sends a test payload
  3. Check your endpoint received the data
  4. Verify data structure is correct

Step 4: Submit Real Form

  1. Go to your published landing page
  2. Fill out and submit the form
  3. Check webhook endpoint received data
  4. Verify processing worked correctly

Webhook Configuration

Webhook URL

Requirements:

  • Must use HTTPS (not HTTP)
  • Must be publicly accessible (not localhost)
  • Should return 2xx status code for success
  • Should respond within 30 seconds

Examples:

https://hooks.zapier.com/hooks/catch/123456/abcdef/
https://hook.us1.make.com/abcdefghijklmnop
https://api.yourdomain.com/webhooks/form-submissions

Webhook Secret (Optional)

A shared secret used to verify webhook authenticity:

How it works:

  1. You set a secret in Fly Super (e.g., my-secret-key-123)
  2. Fly Super includes it in the webhook request header
  3. Your endpoint verifies the secret matches
  4. Reject requests with invalid/missing secrets

Security benefit: Prevents unauthorized parties from sending fake submissions to your endpoint.

Example verification (Node.js):

const webhookSecret = process.env.WEBHOOK_SECRET;
const requestSecret = req.headers['x-webhook-secret'];

if (requestSecret !== webhookSecret) {
  return res.status(401).json({ error: 'Invalid webhook secret' });
}

// Process the submission...

Webhook Payload

Request Format

HTTP Method: POST

Headers:

Content-Type: application/json
X-Webhook-Secret: your-secret-key (if configured)
User-Agent: FlySuper-Webhooks/1.0

Body (JSON):

{
  "submissionId": "abc12345",
  "formId": "form_xyz789",
  "formName": "Contact Form",
  "submittedAt": "2024-01-15T10:30:00Z",
  "data": {
    "name": "John Smith",
    "email": "john@example.com",
    "company": "Acme Inc",
    "message": "Interested in learning more about your product..."
  },
  "metadata": {
    "ipAddress": "192.168.1.1",
    "userAgent": "Mozilla/5.0 ...",
    "landerId": "lander_abc123",
    "landerSlug": "product-landing",
    "referrer": "https://google.com",
    "utmSource": "facebook",
    "utmMedium": "cpc",
    "utmCampaign": "spring-2024"
  }
}

Payload Fields

Top-Level Fields:

  • submissionId - Unique submission ID
  • formId - Form ID
  • formName - Form name (for your reference)
  • submittedAt - ISO 8601 timestamp

Data Object:

  • Contains all form field values
  • Field names as keys (the "name" you configured)
  • Values as submitted by user

Metadata Object:

  • ipAddress - Submitter's IP address
  • userAgent - Submitter's browser/device info
  • landerId - Landing page ID (if submitted through landing page)
  • landerSlug - Landing page slug
  • referrer - Previous page URL
  • utmSource, utmMedium, utmCampaign - UTM tracking parameters (if present in URL)

Handling Webhooks

Response Requirements

Your endpoint must:

  1. Respond quickly: Under 30 seconds (preferably under 5 seconds)
  2. Return 2xx status: 200, 201, or 204 indicates success
  3. Handle errors gracefully: Return appropriate error codes

Success Response

Return 2xx status code to indicate successful processing:

// Node.js/Express example
app.post('/webhooks/form-submissions', async (req, res) => {
  const submission = req.body;

  try {
    // Process the submission
    await saveToDatabase(submission);

    // Return success
    res.status(200).json({ received: true });
  } catch (error) {
    // Return error for retry
    res.status(500).json({ error: error.message });
  }
});

Error Handling

4xx Status Codes (Client Errors):

  • No retry - Indicates permanent failure
  • Use for: invalid data, authentication failures, rate limits exceeded

5xx Status Codes (Server Errors):

  • Will retry - Indicates temporary failure
  • Use for: database errors, third-party API failures, timeouts

Examples:

// Permanent failure - no retry
if (!submission.data.email) {
  return res.status(400).json({ error: 'Email is required' });
}

// Temporary failure - will retry
try {
  await saveToCRM(submission);
} catch (error) {
  return res.status(500).json({ error: 'CRM temporarily unavailable' });
}

Retry Logic

Fly Super automatically retries failed webhooks with exponential backoff.

Retry Schedule

AttemptDelayTotal Time
1 (initial)0s0s
230s30s
31m1m 30s
45m6m 30s
515m21m 30s
6 (final)1h1h 21m 30s

Total retries: 5 retries over ~1.5 hours

When Retries Happen

Retries occur when:

  • HTTP 5xx status code (server error)
  • Request timeout (30 seconds)
  • Network error (DNS failure, connection refused)

No retry when:

  • HTTP 2xx status code (success)
  • HTTP 4xx status code (client error - permanent failure)

Monitoring Retries

View retry status in Fly Super:

  1. Navigate to Submissions
  2. Click on a submission
  3. View Webhook Status:
    • ✅ Success (delivered)
    • ⏳ Pending (retry scheduled)
    • ❌ Failed (all retries exhausted)

Common Integrations

Zapier

Setup:

  1. Create new Zap
  2. Trigger: "Webhooks by Zapier" → "Catch Hook"
  3. Copy webhook URL
  4. Add to Fly Super form
  5. Test with sample submission
  6. Add Zap actions (add to CRM, send email, etc.)

Zapier Example Actions:

  • Add to Google Sheets
  • Create Salesforce lead
  • Send Slack notification
  • Add to Mailchimp list
  • Create Trello card

Make (Integromat)

Setup:

  1. Create new scenario
  2. Add "Webhooks" module → "Custom webhook"
  3. Copy webhook URL
  4. Add to Fly Super form
  5. Test with sample submission
  6. Add scenario modules (process data, send to apps)

Make Example Modules:

  • Add row to Airtable
  • Create HubSpot contact
  • Send email via Gmail
  • Update Notion database
  • Post to Discord channel

Airtable

Direct Integration (Coming Soon)

Current Workaround: Use Zapier or Make to connect Fly Super → Airtable

Zap Setup:

  1. Trigger: Webhooks by Zapier (catch hook)
  2. Action: Airtable → Create Record
  3. Map fields:
    • name → Name field
    • email → Email field
    • company → Company field

Slack

Setup:

  1. Create Slack incoming webhook:
    • Go to Slack App Directory → Incoming Webhooks
    • Choose channel
    • Copy webhook URL
  2. Add webhook URL to Fly Super form
  3. Format message (use webhook middleware or Zapier)

Zapier Example:

  1. Trigger: Webhooks by Zapier
  2. Action: Slack → Send Channel Message
  3. Message format:
New form submission from {{name}}
Email: {{email}}
Company: {{company}}
Message: {{message}}

Custom API

Example Node.js Endpoint:

const express = require('express');
const app = express();

app.use(express.json());

app.post('/api/form-submissions', async (req, res) => {
  const { submissionId, formName, data, metadata } = req.body;

  // Verify webhook secret
  const secret = req.headers['x-webhook-secret'];
  if (secret !== process.env.WEBHOOK_SECRET) {
    return res.status(401).json({ error: 'Unauthorized' });
  }

  try {
    // Save to database
    await db.submissions.create({
      id: submissionId,
      form: formName,
      email: data.email,
      name: data.name,
      company: data.company,
      message: data.message,
      ip: metadata.ipAddress,
      submitted_at: new Date()
    });

    // Send notification email
    await sendEmail({
      to: 'sales@yourcompany.com',
      subject: `New ${formName} submission`,
      body: `
        Name: ${data.name}
        Email: ${data.email}
        Company: ${data.company}
        Message: ${data.message}
      `
    });

    // Return success
    res.status(200).json({
      received: true,
      submissionId
    });

  } catch (error) {
    console.error('Webhook error:', error);

    // Return 500 for retry
    res.status(500).json({
      error: 'Processing failed',
      willRetry: true
    });
  }
});

app.listen(3000);

Testing Webhooks

Test Button

Use the "Test Webhook" button in form settings:

Test payload:

{
  "submissionId": "test_123",
  "formId": "form_xyz",
  "formName": "Your Form Name",
  "submittedAt": "2024-01-15T10:00:00Z",
  "data": {
    "name": "Test User",
    "email": "test@example.com"
  },
  "metadata": {
    "ipAddress": "127.0.0.1",
    "userAgent": "Test",
    "landerId": null,
    "referrer": null
  }
}

What to verify:

  • ✅ Endpoint received the request
  • ✅ Data structure is correct
  • ✅ Authentication/secret works
  • ✅ Processing logic executes
  • ✅ 2xx status code returned

Testing Tools

RequestBin / Webhook.site:

  1. Go to webhook.site
  2. Copy unique URL
  3. Add to Fly Super form webhook settings
  4. Submit test form
  5. View request in webhook.site

ngrok (Local Testing):

  1. Install ngrok: npm install -g ngrok
  2. Start your local server: node server.js (port 3000)
  3. Create tunnel: ngrok http 3000
  4. Use ngrok URL as webhook URL
  5. Submit test form
  6. See requests hit your local server

Postman:

  • Create mock webhook endpoint
  • Test payload structure
  • Verify response handling

Best Practices

Security

  1. Always use HTTPS: Never plain HTTP
  2. Validate webhook secret: Check X-Webhook-Secret header
  3. Sanitize input: Clean/validate data before using
  4. Rate limiting: Prevent abuse on your endpoint
  5. IP whitelist: (Optional) Only accept from Fly Super IPs

Performance

  1. Respond quickly: Return 2xx ASAP, process async
  2. Use queues: For slow operations (send email, call APIs)
  3. Batch processing: If handling many submissions
  4. Monitor timeouts: Ensure < 30 second response

Example (async processing):

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

  // Acknowledge immediately
  res.status(200).json({ received: true });

  // Process asynchronously
  processSubmission(submission).catch(console.error);
});

async function processSubmission(submission) {
  // Slow operations here
  await saveToCRM(submission);
  await sendNotification(submission);
  await updateAnalytics(submission);
}

Reliability

  1. Handle retries gracefully: Use idempotency keys to prevent duplicates
  2. Log everything: Debug issues with submission logs
  3. Monitor failures: Alert on webhook failures
  4. Fallback mechanism: Manually process if webhooks fail

Idempotency example:

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

  // Check if already processed
  const existing = await db.submissions.findOne({ submissionId });
  if (existing) {
    return res.status(200).json({ received: true, duplicate: true });
  }

  // Process new submission
  await processSubmission(req.body);
  res.status(200).json({ received: true });
});

Error Handling

  1. Distinguish permanent vs temporary failures
  2. Return appropriate status codes
  3. Log errors with context
  4. Set up alerts for repeated failures

Troubleshooting

"Webhook Failed" Error

Check:

  1. ✅ URL is correct and HTTPS
  2. ✅ Endpoint is publicly accessible (not localhost)
  3. ✅ Endpoint returns 2xx status code
  4. ✅ Endpoint responds within 30 seconds
  5. ✅ No authentication issues (check secret)

Test:

  • Use webhook.site to confirm payload format
  • Use curl to test your endpoint manually:
curl -X POST https://your-endpoint.com/webhook \
  -H "Content-Type: application/json" \
  -H "X-Webhook-Secret: your-secret" \
  -d '{"test": "data"}'

"Timeout" Error

Causes:

  • Endpoint takes > 30 seconds to respond
  • Slow database queries
  • External API calls taking too long

Solutions:

  • Respond immediately, process asynchronously
  • Optimize slow queries
  • Use background jobs for external calls

"Retries Exhausted"

Means:

  • Webhook failed 6 times (initial + 5 retries)
  • No more automatic retries will happen

Next steps:

  1. Check endpoint logs for errors
  2. Fix the issue
  3. Manually replay the submission (coming soon)

"Invalid Secret"

Causes:

  • Webhook secret doesn't match
  • Header not being read correctly

Solutions:

  • Verify secret in Fly Super settings
  • Check header name: x-webhook-secret (lowercase)
  • Log received headers to debug

Advanced Topics

Webhook Signatures (Coming Soon)

Cryptographically sign webhooks for enhanced security:

X-Webhook-Signature: sha256=abc123...

Verify signature to ensure webhook is from Fly Super.

Webhook Transformations (Coming Soon)

Transform payload before sending to endpoint:

  • Map field names
  • Format values
  • Filter fields
  • Add custom data

Multiple Webhooks (Coming Soon)

Send to multiple endpoints:

  • Primary CRM
  • Backup database
  • Analytics platform
  • Notification service

What's Next?


Quick Reference

SettingPurposeRequired
Webhook URLEndpoint to receive submissionsYes
Webhook SecretShared secret for verificationNo (recommended)
TimeoutMax response time30 seconds
RetriesFailed delivery attempts5 retries
Retry ScheduleDelay between retries30s, 1m, 5m, 15m, 1h
Status CodeMeaningRetry?
2xxSuccessNo
4xxClient error (permanent)No
5xxServer error (temporary)Yes
TimeoutNo response in 30sYes

Pro Tip: Start with webhook.site to understand the payload structure, then build your actual endpoint. Test thoroughly with the test button before going live!