Incoming Webhooks
Receive feedback from external services via HTTP
Incoming Webhooks
Receive feedback from external services like Zapier, GitHub Actions, or any system that can send HTTP requests.
Unlike outgoing webhooks (which send data out when feedback is submitted), incoming webhooks let you send feedback into UserHero from external sources.
Setup
- Go to Settings → Integrations tab
- Scroll to Incoming Webhooks
- Click Create Incoming Webhook
- Enter a name (e.g. "Zapier", "GitHub Actions")
- Copy the Webhook URL and Secret — the secret is only shown once
Each webhook gets a unique URL in the format:
https://userhero.co/api/hooks/{slug}You can optionally assign a default project. If set, all feedback sent to this webhook is routed to that project unless overridden in the request body.
Sending Feedback
Send a POST request to your webhook URL with a JSON body and an HMAC-SHA256 signature.
Request Format
curl -X POST https://userhero.co/api/hooks/{slug} \
-H "Content-Type: application/json" \
-H "X-UserHero-Signature: sha256=$SIGNATURE" \
-d '{
"message": "Bug report from external tool",
"category": "bug",
"rating": 3,
"userEmail": "user@example.com",
"tags": ["external", "zapier"],
"metadata": {
"source": "zapier",
"zap_id": "123"
}
}'Payload Fields
| Field | Type | Required | Description |
|---|---|---|---|
message | string | Yes | Feedback text |
projectId | string | No | Target project ID. Required if the webhook has no default project |
category | string | No | Feedback category (e.g. bug, feature, question) |
rating | number | No | Numeric rating |
userEmail | string | No | Email of the person who submitted the feedback |
tags | string[] | No | Array of tags |
context | object | No | Arbitrary context object (page URL, browser info, etc.) |
metadata | object | No | Arbitrary metadata stored with the feedback item |
Response
On success:
{
"success": true,
"feedbackId": "abc123"
}On error:
| Status | Meaning |
|---|---|
400 | Invalid JSON, missing message, or missing projectId |
401 | Missing or invalid signature |
404 | Webhook not found or disabled |
500 | Server error |
Signature Verification
Every request must include an X-UserHero-Signature header. This proves the request comes from someone who knows the webhook secret.
Computing the Signature
Sign the raw JSON body with HMAC-SHA256 using your webhook secret:
const crypto = require('crypto');
const body = JSON.stringify({ message: 'Bug report' });
const signature = crypto
.createHmac('sha256', 'whsec_your_secret')
.update(body)
.digest('hex');
// Send as header:
// X-UserHero-Signature: sha256=<signature>import hmac, hashlib, json
body = json.dumps({"message": "Bug report"})
signature = hmac.new(
b"whsec_your_secret",
body.encode(),
hashlib.sha256
).hexdigest()
# Send as header:
# X-UserHero-Signature: sha256=<signature>BODY='{"message":"Bug report"}'
SIGNATURE=$(echo -n "$BODY" | openssl dgst -sha256 -hmac "whsec_your_secret" | awk '{print $2}')
curl -X POST https://userhero.co/api/hooks/{slug} \
-H "Content-Type: application/json" \
-H "X-UserHero-Signature: sha256=$SIGNATURE" \
-d "$BODY"The header value can be sha256=<hex> or just <hex>.
Use Cases
Zapier / Make / n8n
Connect any trigger (new form submission, new email, Slack message) to UserHero:
- Create an incoming webhook in UserHero
- In your automation tool, add an HTTP POST action
- Set the URL to your webhook URL
- Add the
X-UserHero-Signatureheader with the computed HMAC - Map fields from the trigger to the JSON body
CI/CD Pipelines
Automatically log deployment feedback or test results:
# In your GitHub Actions workflow
- name: Send deployment feedback
run: |
BODY='{"message":"Deployed v1.2.3 to production","category":"deployment","tags":["ci","production"]}'
SIG=$(echo -n "$BODY" | openssl dgst -sha256 -hmac "${{ secrets.USERHERO_WEBHOOK_SECRET }}" | awk '{print $2}')
curl -X POST ${{ secrets.USERHERO_WEBHOOK_URL }} \
-H "Content-Type: application/json" \
-H "X-UserHero-Signature: sha256=$SIG" \
-d "$BODY"Custom Applications
Forward feedback from internal tools, support systems, or other products:
const crypto = require('crypto');
async function sendToUserHero(message, metadata) {
const body = JSON.stringify({ message, metadata });
const signature = crypto
.createHmac('sha256', process.env.USERHERO_WEBHOOK_SECRET)
.update(body)
.digest('hex');
const res = await fetch(process.env.USERHERO_WEBHOOK_URL, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-UserHero-Signature': `sha256=${signature}`,
},
body,
});
return res.json();
}Managing via REST API
You can also create and manage incoming webhooks programmatically using the REST API:
# List webhooks
curl -H "Authorization: Bearer sk_live_..." \
"https://userhero.co/api/v1/webhooks?workspaceId=ws_123"
# Create webhook
curl -X POST https://userhero.co/api/v1/webhooks \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{"workspaceId":"ws_123","name":"My Webhook","projectId":"proj_456"}'
# Delete webhook
curl -X DELETE "https://userhero.co/api/v1/webhooks/{id}?workspaceId=ws_123" \
-H "Authorization: Bearer sk_live_..."