Skip to main content

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

  1. Go to your Feedbackfirst workspace
  2. Click Settings → Webhooks
  3. Click "Add Webhook"
  4. Enter your endpoint URL
  5. Select which events to subscribe to
  6. 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

  1. Go to Settings → Webhooks
  2. Click the webhook
  3. Click "Send Test Event"
  4. 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);
});