Skip to main content
Webhooks let you send form submissions to your own server or third-party services.

Setup

  1. Go to your form settings
  2. Enable webhooks
  3. Enter your webhook URL
  4. Optionally add a webhook secret for signature verification

Payload Format

Spike sends a POST request with JSON body:
{
  "event": "submission.created",
  "formId": "clx123abc",
  "formName": "Contact Form",
  "formSlug": "abc123xyz",
  "submissionId": "sub_xyz789",
  "data": {
    "email": "[email protected]",
    "name": "John Doe",
    "message": "Hello, I have a question..."
  },
  "metadata": {
    "ip": "1.2.3.4",
    "userAgent": "Mozilla/5.0...",
    "referrer": "https://yoursite.com/contact",
    "country": "US",
    "timestamp": "2024-01-15T10:30:00.000Z"
  }
}

Signature Verification

If you set a webhook secret, Spike includes a signature header for verification:
X-Spike-Signature: sha256=abc123...
Verify the signature in your webhook handler:
import crypto from 'crypto';

function verifySignature(payload, signature, secret) {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex');
  
  return `sha256=${expected}` === signature;
}

// Express.js example
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
  const signature = req.headers['x-spike-signature'];
  const isValid = verifySignature(req.body, signature, process.env.WEBHOOK_SECRET);
  
  if (!isValid) {
    return res.status(401).send('Invalid signature');
  }
  
  const data = JSON.parse(req.body);
  // Process the webhook...
  
  res.status(200).send('OK');
});

Retries

Spike automatically retries failed webhooks with exponential backoff:
AttemptDelay
1Immediate
21 minute
35 minutes
430 minutes
A webhook is considered failed if:
  • Response status is not 2xx
  • Request times out (30 seconds)
  • Connection error

Response

Your webhook should return a 2xx status code to acknowledge receipt. The response body is ignored.

Testing

Test your webhook locally using ngrok:
ngrok http 3000
Then use the ngrok URL as your webhook URL during development.

Example Handlers

Node.js / Express

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

app.post('/webhook', express.json(), (req, res) => {
  const { event, data, metadata } = req.body;
  
  console.log('New submission:', data.email);
  
  // Process the submission...
  
  res.status(200).send('OK');
});

Python / Flask

from flask import Flask, request

app = Flask(__name__)

@app.route('/webhook', methods=['POST'])
def webhook():
    data = request.json
    
    print(f"New submission: {data['data']['email']}")
    
    # Process the submission...
    
    return 'OK', 200

Next.js API Route

// app/api/webhook/route.ts
import { NextResponse } from 'next/server';
import crypto from 'crypto';

export async function POST(request: Request) {
  const body = await request.text();
  const signature = request.headers.get('x-spike-signature');
  
  // Verify signature
  const expected = crypto
    .createHmac('sha256', process.env.WEBHOOK_SECRET!)
    .update(body)
    .digest('hex');
  
  if (`sha256=${expected}` !== signature) {
    return NextResponse.json({ error: 'Invalid signature' }, { status: 401 });
  }
  
  const data = JSON.parse(body);
  
  // Process the submission...
  console.log('New submission:', data.data.email);
  
  return NextResponse.json({ received: true });
}