Developer Documentation

FormFork Integration Guide & API Reference

Integrate FormFork endpoints into any HTML form, React application, Webflow site, or backend script in seconds.

Form Ingestion Methods

1. Standard HTML Form POST

<form action="https://formfork.dev/f/YOUR_FORM_ID" method="POST">
  <input type="text" name="name" placeholder="Your Name" required />
  <input type="email" name="email" placeholder="Your Email" required />
  <textarea name="message" placeholder="Message"></textarea>
  
  <!-- Honeypot Bot Trap -->
  <input type="text" name="_gotcha" style="display:none" tabindex="-1" autocomplete="off" />

  <button type="submit">Submit</button>
</form>

2. React / Next.js JSON Fetch

const response = await fetch('https://formfork.dev/f/YOUR_FORM_ID', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Accept': 'application/json'
  },
  body: JSON.stringify({
    name: 'Jane Doe',
    email: 'jane@example.com',
    message: 'Hello FormFork'
  })
});

const data = await response.json();
console.log('Submission ID:', data.submissionId);
Real-time Integration

Outbound Webhook Delivery & Payloads

When a lead submits to your endpoint, FormFork dispatches an HTTP POST request to your designated Webhook URL within 50ms.

HTTP Headers Sent by FormFork

Header NameDescriptionExample Value
Content-TypePayload formatapplication/json
X-FormFork-EventEvent type descriptorform.submission.created
X-FormFork-Delivery-IDUnique delivery event IDdel_982340182
X-FormFork-SignatureHMAC-SHA256 timestamp & signature for verificationt=1728000000,v1=a3b8c9f2...

Sample JSON Webhook Payload

{
  "event": "form.submission.created",
  "id": "del_982340182",
  "timestamp": "2026-10-03T11:42:00.000Z",
  "form": {
    "id": "form-contact-demo",
    "name": "Website Contact Us Form"
  },
  "submission": {
    "id": "sub-101",
    "data": {
      "name": "Sarah Jenkins",
      "email": "sarah.j@acmecorp.io",
      "subject": "Enterprise Webhooks Integration",
      "message": "Looking to route leads directly into our internal CRM pipeline."
    },
    "metadata": {
      "ipAddress": "192.168.1.45",
      "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)",
      "referer": "https://acmecorp.io/contact"
    },
    "spam": {
      "isSpam": false,
      "score": 0.02
    }
  }
}

X-FormFork-Signature Verification (Node.js)

const crypto = require('crypto');

function verifyFormForkWebhook(rawBody, signatureHeader, webhookSecret) {
  // Format: t=TIMESTAMP,v1=SIGNATURE
  const parts = Object.fromEntries(
    signatureHeader.split(',').map(p => p.split('='))
  );

  const timestamp = parts.t;
  const receivedSignature = parts.v1;

  // Build signed payload string
  const signedPayload = `${timestamp}.${rawBody}`;
  
  // Calculate expected HMAC SHA-256 signature
  const expectedSignature = crypto
    .createHmac('sha256', webhookSecret)
    .update(signedPayload)
    .digest('hex');

  return crypto.timingSafeEqual(
    Buffer.from(receivedSignature),
    Buffer.from(expectedSignature)
  );
}
Governance & Traffic

API Rate Limits & HTTP 429 Handling

FormFork enforces sliding-window rate limits per IP address and form endpoint to protect against DDoS attacks, automated spam floods, and resource exhaustion.

Rate Limits per Plan Tier

Plan TierMax API ThroughputMonthly Ingestion LimitHTTP Rate Limit Behavior
Free ($0)60 req / min (1 req/sec)1,000 / monthReturns HTTP 429 when burst limit exceeded
Growth ($12/mo)300 req / min (5 req/sec)20,000 / monthReturns HTTP 429 with Retry-After header
Agency ($39/mo)1,200 req / min (20 req/sec)UnlimitedHigh-throughput queue with auto burst-smoothing
HTTP 429

429 Too Many Requests Response Headers

When an endpoint exceeds its rate limit, FormFork returns an HTTP status 429 with telemetry headers indicating when requests may resume:

HTTP/1.1 429 Too Many Requests
Content-Type: application/json
Retry-After: 60
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1728000060

{
  "success": false,
  "error": "rate_limit_exceeded",
  "message": "Submission rate limit exceeded. Please retry after 60 seconds.",
  "retryAfterSeconds": 60
}

How Developers Should Handle 429s

1. Inspect the Retry-After Header

Always check response.headers.get('Retry-After') before attempting a retry. Pause client requests for the indicated duration.

2. Implement Exponential Backoff with Jitter

In custom frontend forms or mobile apps, retry failed submissions using exponential backoff (e.g., 1s, 2s, 4s) plus random jitter to avoid thundering herd problems.

3. Upgrade Plan Tier for High Traffic

If your website consistently experiences high submission concurrency (e.g. flash sales or viral marketing campaigns), upgrade to the Growth or Agency tier for up to 1,200 requests/min.