Webhooks
Webhooks allow you to listen to events on Feedbackfirst and trigger actions in your application. When something happens (like feedback being submitted or validated), we'll send an HTTP POST request to your webhook URL.
Supported Events
feedback.submitted
Fired when a reviewer submits feedback on your product.
Payload:
{
"event": "feedback.submitted",
"timestamp": "2024-04-08T10:30:00Z",
"data": {
"feedback_id": "fb-123",
"product_id": "prod-456",
"reviewer_id": "user-789",
"reviewer_name": "John Doe",
"reviewer_email": "john@example.com",
"type": "structured",
"content": {
"clear": "The onboarding is intuitive.",
"confusing": "The pricing page is unclear.",
"works": "The dashboard is fast.",
"improve": "Add keyboard shortcuts."
}
}
}
feedback.validated
Fired when you validate feedback (approve it as useful).
Payload:
{
"event": "feedback.validated",
"timestamp": "2024-04-08T10:35:00Z",
"data": {
"feedback_id": "fb-123",
"product_id": "prod-456",
"reviewer_id": "user-789",
"reviewer_name": "John Doe",
"credits_earned": 5
}
}
feedback.dismissed
Fired when you dismiss feedback (mark it as not useful).
Payload:
{
"event": "feedback.dismissed",
"timestamp": "2024-04-08T10:35:00Z",
"data": {
"feedback_id": "fb-123",
"product_id": "prod-456",
"reviewer_id": "user-789"
}
}
survey.submitted
Fired when a user submits a survey response.
Payload:
{
"event": "survey.submitted",
"timestamp": "2024-04-08T10:40:00Z",
"data": {
"survey_id": "survey-123",
"product_id": "prod-456",
"respondent_id": "user-789",
"respondent_name": "Jane Doe",
"respondent_email": "jane@example.com",
"responses": {
"q1": "Very satisfied",
"q2": "8",
"q3": "The UI could be simpler"
}
}
}
product.published
Fired when a product is published.
Payload:
{
"event": "product.published",
"timestamp": "2024-04-08T10:45:00Z",
"data": {
"product_id": "prod-456",
"maker_id": "user-123",
"product_name": "Feedbackfirst",
"product_url": "https://feedbackfirst.dev",
"category": "SaaS",
"status": "MVP"
}
}
Setting Up Webhooks
Step 1: Create a Webhook Endpoint
Create an endpoint on your server that accepts POST requests:
app.post('/webhooks/feedbackfirst', (req, res) => {
const event = req.body;
console.log(`Received event: ${event.event}`);
// Verify the webhook signature (see below)
if (!verifyWebhookSignature(req)) {
return res.status(401).json({ error: 'Invalid signature' });
}
// Handle the event
switch (event.event) {
case 'feedback.submitted':
handleFeedbackSubmitted(event.data);
break;
case 'feedback.validated':
handleFeedbackValidated(event.data);
break;
// ... handle other events
}
// Return 200 to acknowledge receipt
res.json({ success: true });
});
Step 2: Register Your Webhook
- Go to your Feedbackfirst workspace
- Click Settings → Webhooks
- Click "Add Webhook"
- Enter your endpoint URL
- Select which events to subscribe to
- Copy your webhook secret
Step 3: Verify Webhook Signatures
Feedbackfirst signs all webhooks with your secret. Verify the signature to ensure the webhook is authentic:
const crypto = require('crypto');
function verifyWebhookSignature(req) {
const signature = req.headers['x-feedbackfirst-signature'];
const secret = process.env.FEEDBACKFIRST_WEBHOOK_SECRET;
const hash = crypto
.createHmac('sha256', secret)
.update(JSON.stringify(req.body))
.digest('hex');
return hash === signature;
}
Handling Webhooks
Idempotency
Webhooks may be delivered multiple times. Make your webhook handler idempotent:
app.post('/webhooks/feedbackfirst', async (req, res) => {
const event = req.body;
const eventId = event.id; // Unique event ID
// Check if we've already processed this event
const processed = await db.webhookEvents.findOne({ eventId });
if (processed) {
return res.json({ success: true });
}
// Process the event
await handleEvent(event);
// Mark as processed
await db.webhookEvents.create({ eventId, timestamp: new Date() });
res.json({ success: true });
});
Retry Logic
If your endpoint returns a non-2xx status code, Feedbackfirst will retry:
- Immediately
- After 5 seconds
- After 5 minutes
- After 30 minutes
- After 2 hours
After 5 failed attempts, the webhook is disabled.
Timeout
Your endpoint must respond within 30 seconds. If it takes longer, Feedbackfirst will timeout and retry.
Use Cases
Sync Feedback to Your CRM
function handleFeedbackSubmitted(data) {
// Send feedback to your CRM
crm.createNote({
contact_id: data.reviewer_id,
title: `Feedback on ${data.product_id}`,
content: `${data.content.clear}\n${data.content.confusing}\n${data.content.works}\n${data.content.improve}`
});
}
Send Notifications
function handleFeedbackValidated(data) {
// Send Slack notification
slack.send({
channel: '#feedback',
text: `${data.reviewer_name} left validated feedback on your product. They earned ${data.credits_earned} credits.`
});
}
Update Your Database
function handleSurveySubmitted(data) {
// Store survey response in your database
db.surveyResponses.create({
survey_id: data.survey_id,
respondent_id: data.respondent_id,
responses: data.responses,
timestamp: new Date()
});
}
Trigger Workflows
function handleProductPublished(data) {
// Trigger a workflow in your automation tool
zapier.trigger('feedbackfirst_product_published', {
product_name: data.product_name,
product_url: data.product_url,
maker_id: data.maker_id
});
}
Testing Webhooks
Manual Test
- Go to Settings → Webhooks
- Click the webhook
- Click "Send Test Event"
- Check your server logs
Using ngrok
Test locally with ngrok:
# Start ngrok
ngrok http 3000
# Register webhook with ngrok URL
# https://abc123.ngrok.io/webhooks/feedbackfirst
# Trigger events and check logs
Best Practices
Always Verify Signatures
Never trust webhooks without verifying the signature.
Handle Errors Gracefully
app.post('/webhooks/feedbackfirst', async (req, res) => {
try {
const event = req.body;
await handleEvent(event);
res.json({ success: true });
} catch (error) {
console.error('Webhook error:', error);
res.status(500).json({ error: 'Processing failed' });
}
});
Log All Webhooks
app.post('/webhooks/feedbackfirst', (req, res) => {
const event = req.body;
console.log(`[${new Date().toISOString()}] Webhook: ${event.event}`);
// ... handle event
});
Monitor Webhook Health
Track failed webhooks and retry manually if needed.
Use a Queue
For heavy processing, queue webhook events and process them asynchronously:
app.post('/webhooks/feedbackfirst', (req, res) => {
const event = req.body;
queue.add('process-webhook', event);
res.json({ success: true });
});
queue.process('process-webhook', async (job) => {
await handleEvent(job.data);
});