Webhooks
Receive real-time HTTP callbacks when events happen in your Helpr workspace — new conversations, messages, and more.
Overview
Webhooks let you subscribe to events in your Helpr workspace. When an event occurs — a visitor starts a conversation, sends a message, or an agent replies — Helpr sends a POST request to your endpoint with the event data.
Use webhooks to sync conversations to your CRM, trigger notifications in Slack, log messages to your data warehouse, or build custom integrations.
Setup
- Go to Settings → Developer in the Helpr dashboard
- Scroll to the Webhooks section
- Click Add Webhook
- Enter your HTTPS endpoint URL
- Select the events you want to receive
- Click Create — copy and save the signing secret immediately
Your endpoint must use HTTPS and respond with a 2xx status code within 10 seconds.
Important: The signing secret is only shown once when you create the webhook. You’ll need it to verify signatures on incoming requests.
Managing Webhooks
All webhook management is done in the Helpr dashboard under Settings → Developer → Webhooks:
- Pause / Resume — temporarily stop deliveries without deleting the webhook
- Edit — change the endpoint URL or subscribed events
- Delete — permanently remove the webhook and its delivery history
If a webhook accumulates 100 consecutive delivery failures, it is automatically disabled. You can re-enable it from the dashboard after fixing your endpoint.
chat.created
Fired when a visitor starts a new conversation (first message in a chat).
{
"event": "chat.created",
"timestamp": "2026-04-27T14:32:10+00:00",
"data": {
"chat_id": 4521,
"team_id": 3,
"visitor_id": "v_8f3a2b1c",
"identity": {
"email": "[email protected]",
"name": "Jane Smith",
"userId": "usr_123",
"company": "Acme Inc"
}
}
}
The identity object is included when the visitor has been identified via helpr.identify(). It contains whichever fields were set: email, name, userId, company, phone. If the visitor is anonymous, the identity field is omitted.
chat.message
Fired on every public message in a conversation — from visitors, agents, or bots.
{
"event": "chat.message",
"timestamp": "2026-04-27T14:32:10+00:00",
"data": {
"chat_id": 4521,
"message_id": 18923,
"sender_type": "visitor",
"sender_name": "Jane Smith",
"body": "Hi, I need help with my order",
"identity": {
"email": "[email protected]",
"name": "Jane Smith",
"userId": "usr_123"
}
}
}
| Field | Description |
|---|---|
chat_id | The conversation ID |
message_id | Unique message ID |
sender_type | visitor, agent, or bot |
sender_name | Display name of the sender |
body | Message text content |
identity | Visitor identity object (omitted if anonymous). See chat.created for available fields |
Wildcard Subscription
Pass ["*"] as the events array to receive every event type, including any added in the future.
{
"url": "https://your-server.com/helpr/webhook",
"events": ["*"]
}
Request Format
Every webhook delivery is an HTTP POST with a JSON body and these headers:
| Header | Example | Description |
|---|---|---|
Content-Type | application/json | Always JSON |
X-Helpr-Signature | sha256=a1b2c3d4... | HMAC-SHA256 signature of the request body |
X-Helpr-Event | chat.message | The event type |
X-Helpr-Delivery | 98231 | Unique delivery ID (useful for deduplication) |
Signature Verification
Every request is signed with your webhook’s secret using HMAC-SHA256. Always verify signatures before processing the payload.
const crypto = require('crypto');
function verifySignature(payload, signature, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}
// In your request handler:
app.post('/helpr/webhook', (req, res) => {
const sig = req.headers['x-helpr-signature']?.replace('sha256=', '');
if (!sig || !verifySignature(req.rawBody, sig, process.env.HELPR_WEBHOOK_SECRET)) {
return res.status(401).send('Invalid signature');
}
const { event, data } = JSON.parse(req.rawBody);
switch (event) {
case 'chat.created':
console.log('New chat:', data.chat_id);
break;
case 'chat.message':
console.log(`${data.sender_name}: ${data.body}`);
break;
}
res.status(200).send('ok');
});
import hmac, hashlib, os
def verify(payload: bytes, signature: str, secret: str) -> bool:
expected = hmac.new(
secret.encode(), payload, hashlib.sha256
).hexdigest()
return hmac.compare_digest(signature, expected)
# In your handler:
sig = request.headers.get('X-Helpr-Signature', '').removeprefix('sha256=')
if not verify(request.data, sig, os.environ['HELPR_WEBHOOK_SECRET']):
return 'Invalid signature', 401
data = request.json
print(f"Event: {data['event']}, Data: {data['data']}")
$payload = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_HELPR_SIGNATURE'] ?? '';
$secret = getenv('HELPR_WEBHOOK_SECRET');
$expected = 'sha256=' . hash_hmac('sha256', $payload, $secret);
if (!hash_equals($expected, $signature)) {
http_response_code(401);
exit('Invalid signature');
}
$event = $_SERVER['HTTP_X_HELPR_EVENT'] ?? '';
$data = json_decode($payload, true);
// Handle the event...
switch ($data['event']) {
case 'chat.created':
// New conversation started
break;
case 'chat.message':
// New message received
break;
}
Retries & Failures
Helpr expects a 2xx response within 10 seconds. If your endpoint fails or times out, delivery is retried with exponential backoff:
| Attempt | Retry after |
|---|---|
| 1st failure | 10 seconds |
| 2nd failure | 60 seconds |
| 3rd failure | 5 minutes |
| 4th failure | Marked as failed |
After 100 consecutive failures across all deliveries, the webhook is automatically disabled. A single successful delivery resets the failure counter to zero.
To re-enable a disabled webhook, update its status to active.
Best Practices
- Return 200 quickly. Process events asynchronously — queue the payload, respond immediately. If your handler takes more than 10 seconds, the delivery is marked as failed.
- Deduplicate with the delivery ID. The same event may be delivered more than once during retries. Use the
X-Helpr-Deliveryheader to detect duplicates. - Always verify signatures. Validate
X-Helpr-Signaturebefore trusting the payload. Use constant-time comparison to prevent timing attacks. - Use HTTPS. Webhook payloads contain conversation data. Only HTTPS endpoints are accepted.
- Monitor your endpoint. If your endpoint starts failing, Helpr disables the webhook after 100 consecutive failures. Set up alerting on your side.