Integrations
Webhooks
Send feedback to custom endpoints
Webhooks
Send feedback data to any HTTP endpoint for custom integrations.
Setup
- Go to project Settings → Integrations
- Find Webhooks and click "Add Webhook"
- Enter your webhook URL
- Optionally add a secret for verification
- Test the webhook
- Save
Webhook Payload
When feedback is submitted, we send a POST request:
{
"event": "feedback.created",
"timestamp": "2026-01-07T15:30:00Z",
"data": {
"id": "fb_abc123",
"projectId": "proj_xyz",
"widgetId": "w_default",
"message": "The export button doesn't work",
"category": "bug",
"rating": 2,
"status": "new",
"screenshotUrl": "https://storage.userhero.co/...",
"context": {
"pageUrl": "/dashboard/reports",
"referrer": "/dashboard",
"browser": "Chrome",
"browserVersion": "120",
"os": "macOS",
"deviceType": "desktop",
"viewport": {
"width": 1920,
"height": 1080
},
"timezone": "America/New_York",
"locale": "en-US",
"country": "US"
},
"metadata": {
"userId": "user_123",
"plan": "pro",
"company": "Acme Inc"
},
"createdAt": "2026-01-07T15:30:00Z"
}
}Verifying Webhooks
We sign webhook payloads with your secret. Verify the signature to ensure authenticity.
Signature Header
X-UserHero-Signature: sha256=abc123...Verification Code
const crypto = require('crypto');
function verifyWebhook(payload, signature, secret) {
const expected = 'sha256=' + crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}
// In your webhook handler
app.post('/webhook/userhero', (req, res) => {
const signature = req.headers['x-userhero-signature'];
const isValid = verifyWebhook(
JSON.stringify(req.body),
signature,
process.env.USERHERO_WEBHOOK_SECRET
);
if (!isValid) {
return res.status(401).send('Invalid signature');
}
// Process the webhook
const { event, data } = req.body;
console.log('Received:', event, data);
res.status(200).send('OK');
});Event Types
| Event | Description |
|---|---|
feedback.created | New feedback submitted |
feedback.updated | Feedback status changed |
Response Requirements
Your endpoint should:
- Return a 2xx status code within 30 seconds
- Return quickly (process asynchronously if needed)
Retry Policy
If your endpoint fails, we retry:
| Attempt | Delay |
|---|---|
| 1st retry | 1 minute |
| 2nd retry | 5 minutes |
| 3rd retry | 30 minutes |
| 4th retry | 2 hours |
| 5th retry | 24 hours |
After 5 failed attempts, the webhook is marked as failing and you'll receive an email notification.
Testing Webhooks
Test Button
- Go to your webhook configuration
- Click "Send Test"
- Check your endpoint received the test payload
Local Development
For local testing, use a tunnel service:
# Using ngrok
ngrok http 3000
# Then use the ngrok URL in your webhook config
https://abc123.ngrok.io/webhook/userheroUse Cases
Zapier-style Automation
Connect to no-code tools:
UserHero → Webhook → Zapier → Google SheetsCustom Dashboard
Store feedback in your own database:
app.post('/webhook/userhero', async (req, res) => {
const { data } = req.body;
await db.feedback.create({
externalId: data.id,
message: data.message,
userId: data.metadata?.userId,
createdAt: data.createdAt,
});
res.status(200).send('OK');
});Issue Tracker Integration
Create tickets in your custom system:
app.post('/webhook/userhero', async (req, res) => {
const { data } = req.body;
if (data.category === 'bug') {
await issueTracker.createTicket({
title: `Bug Report: ${data.message.slice(0, 50)}...`,
description: data.message,
priority: data.rating <= 2 ? 'high' : 'normal',
metadata: { userheroId: data.id },
});
}
res.status(200).send('OK');
});Managing Webhooks
Disable Temporarily
- Go to webhook settings
- Toggle "Enabled" off
- Webhook stops receiving events
Delete
- Go to webhook settings
- Click "Delete"
- Confirm deletion