Testing webhooks during development requires your endpoint to be reachable from the public internet. This guide covers several approaches, from quick inspection tools to full local development setups.
Option 1: webhook.site (Quick Inspection)
webhook.site provides a temporary public URL that captures and displays incoming HTTP requests. This is the fastest way to see what NueForm sends without writing any code.
- Go to webhook.site.
- Copy the unique URL (e.g.,
https://webhook.site/abc123-def456-...). - Set it as your form's webhook URL:
curl -X PUT https://app.nueform.com/api/v1/webhooks/form/YOUR_FORM_ID \
-H "Authorization: Bearer nf_your_api_key" \
-H "Content-Type: application/json" \
-d '{ "url": "https://webhook.site/abc123-def456-..." }'
- Submit a response to your form.
- Refresh webhook.site to see the captured request, including headers, body, and the
X-NueForm-Signature.
webhook.site is great for inspection but does not let you run custom verification logic. Use it to understand the payload format, then move to a local server for full testing.
Option 2: ngrok (Local Development)
ngrok creates a secure tunnel from a public URL to your local machine. This lets you receive real webhook deliveries on your development server.
Setup
- Install ngrok:
# macOS (Homebrew)
brew install ngrok
# Or download from https://ngrok.com/download
- Start your local webhook server (e.g., on port 3001):
node server.js
# or
python app.py
- Start an ngrok tunnel:
ngrok http 3001
- Copy the HTTPS forwarding URL from the ngrok output:
Forwarding https://a1b2c3d4.ngrok-free.app -> http://localhost:3001
- Set the ngrok URL as your webhook endpoint:
curl -X PUT https://app.nueform.com/api/v1/webhooks/form/YOUR_FORM_ID \
-H "Authorization: Bearer nf_your_api_key" \
-H "Content-Type: application/json" \
-d '{ "url": "https://a1b2c3d4.ngrok-free.app/webhooks/nueform" }'
- Submit a response to your form. The webhook will arrive at your local server.
Inspecting Traffic
ngrok provides a local web interface at http://localhost:4040 where you can inspect all requests passing through the tunnel, replay them, and view headers and response codes.
Free ngrok URLs change every time you restart ngrok. Remember to update your webhook URL in NueForm when you get a new tunnel URL. Consider upgrading to a paid ngrok plan for a stable subdomain.
Option 3: curl (Simulating Payloads)
You can use curl to send test webhook payloads to your local server without going through NueForm at all. This is useful for testing your verification and processing logic in isolation.
Generating a Signed Test Payload
First, create a test payload and sign it with your webhook secret:
# Your webhook secret (from the NueForm API or dashboard)
SECRET="a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2"
# The test payload
PAYLOAD='{
"event": "form.submitted",
"formId": "507f1f77bcf86cd799439011",
"formTitle": "Test Form",
"responseId": "507f1f77bcf86cd799439022",
"answers": [
{ "questionId": "507f1f77bcf86cd799439033", "value": "Test answer" },
{ "questionId": "507f1f77bcf86cd799439044", "value": 5 }
],
"submittedAt": "2025-03-15T14:32:07.123Z"
}'
# Compute the HMAC-SHA256 signature
SIGNATURE=$(echo -n "$PAYLOAD" | openssl dgst -sha256 -hmac "$SECRET" | awk '{print $2}')
echo "Signature: $SIGNATURE"
Sending the Signed Request
curl -X POST http://localhost:3001/webhooks/nueform \
-H "Content-Type: application/json" \
-H "X-NueForm-Signature: $SIGNATURE" \
-d "$PAYLOAD"
One-Liner
Combine everything into a single command:
SECRET="your_secret_here"
PAYLOAD='{"event":"form.submitted","formId":"507f1f77bcf86cd799439011","formTitle":"Test Form","responseId":"507f1f77bcf86cd799439022","answers":[{"questionId":"q1","value":"hello"}],"submittedAt":"2025-03-15T14:32:07.123Z"}'
SIGNATURE=$(echo -n "$PAYLOAD" | openssl dgst -sha256 -hmac "$SECRET" | awk '{print $2}')
curl -X POST http://localhost:3001/webhooks/nueform \
-H "Content-Type: application/json" \
-H "X-NueForm-Signature: $SIGNATURE" \
-d "$PAYLOAD"
Testing Signature Rejection
To verify that your endpoint correctly rejects invalid signatures, send a request with a wrong signature:
curl -X POST http://localhost:3001/webhooks/nueform \
-H "Content-Type: application/json" \
-H "X-NueForm-Signature: 0000000000000000000000000000000000000000000000000000000000000000" \
-d '{"event":"form.submitted","formId":"test","formTitle":"Test","responseId":"test","answers":[],"submittedAt":"2025-03-15T14:32:07.123Z"}'
Your endpoint should return 401 Unauthorized.
Option 4: Node.js Test Script
Create a standalone Node.js script to quickly sign and send test payloads:
import crypto from 'crypto';
const SECRET = process.env.NUEFORM_WEBHOOK_SECRET || 'your_secret_here';
const ENDPOINT = process.env.WEBHOOK_URL || 'http://localhost:3001/webhooks/nueform';
const payload = JSON.stringify({
event: 'form.submitted',
formId: '507f1f77bcf86cd799439011',
formTitle: 'Customer Feedback Survey',
responseId: crypto.randomUUID().replace(/-/g, '').slice(0, 24),
answers: [
{ questionId: 'q_name', value: 'Jane Doe' },
{ questionId: 'q_email', value: 'jane@example.com' },
{ questionId: 'q_rating', value: 4 },
{ questionId: 'q_feedback', value: 'Great product!' },
{ questionId: 'q_features', value: ['Feature A', 'Feature C'] },
],
submittedAt: new Date().toISOString(),
});
const signature = crypto
.createHmac('sha256', SECRET)
.update(payload)
.digest('hex');
const response = await fetch(ENDPOINT, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-NueForm-Signature': signature,
},
body: payload,
});
console.log(`Status: ${response.status}`);
console.log(`Body: ${await response.text()}`);
Run it with:
NUEFORM_WEBHOOK_SECRET=your_secret node test-webhook.mjs
Option 5: Trigger a Real Submission
The most thorough way to test is to submit an actual response to your form:
- Configure your webhook URL (per-form or global) to point to your test endpoint.
- Open your published form in a browser.
- Fill out and submit the form.
- Observe the webhook delivery on your endpoint.
This tests the entire pipeline end-to-end, including answer validation, quiz scoring, and the actual payload NueForm generates.
Debugging Failed Deliveries
If your webhook endpoint is not receiving requests, work through this checklist:
1. Verify the URL is configured
# Check per-form webhook
curl https://app.nueform.com/api/v1/webhooks/form/YOUR_FORM_ID \
-H "Authorization: Bearer nf_your_api_key"
# Check global webhooks
curl https://app.nueform.com/api/v1/webhooks/global \
-H "Authorization: Bearer nf_your_api_key"
2. Verify the URL is reachable
# Test that your endpoint accepts POST requests
curl -X POST https://your-endpoint.com/webhooks/nueform \
-H "Content-Type: application/json" \
-d '{"test": true}'
3. Check for a webhook secret
Webhooks are only dispatched if your account has a webhook secret set. Verify:
curl https://app.nueform.com/api/v1/webhooks/secret \
-H "Authorization: Bearer nf_your_api_key"
If the response shows a secret, you are good. If not, one will be auto-generated by this request.
4. Check your plan
Webhooks require a Pro plan or higher. Verify your plan status in the NueForm dashboard under your account settings.
5. Check the timeout
NueForm has a 5-second timeout. If your endpoint takes longer to respond, the request will be aborted. Make sure you return 200 OK immediately and process data in the background.
6. Check firewall and network rules
Ensure your server allows incoming POST requests from external sources. If you are behind a firewall or VPN, you may need to allowlist NueForm's IP ranges or use ngrok.
Common Testing Mistakes
| Mistake | Solution |
|---|---|
Using express.json() middleware before signature verification | Use express.raw({ type: 'application/json' }) on the webhook route |
| Testing with a revoked or expired API key | Generate a fresh API key |
| Forgetting to enable global webhooks | Set "enabled": true on each global webhook entry |
| Using HTTP instead of HTTPS for the webhook URL | NueForm sends to whatever URL you provide, but use HTTPS in production |
| Not checking for a webhook secret | Ensure your account has a webhook secret before expecting deliveries |
Next Steps
- Overview --- How webhooks work in NueForm
- Payloads --- Understand the payload format
- Verification --- Implement signature verification