Configuration
1
Navigate to webhooks
Go to Admin → Integrations → Webhooks in your libredesk dashboard
2
Create webhook
Click New webhook and fill in:
- Name: a name you will recognize later
- URL: where libredesk sends the payload
- Events: the events to subscribe to
- Secret: optional, used to sign the payload
- Active: enabled or disabled
3
Test webhook
Open the webhook’s menu in the list and click Send test to send a
webhook.test event to your endpoint.Security
Signature verification
When you set a secret, libredesk signs the payload with HMAC-SHA256 and sends the result in theX-Libredesk-Signature header as sha256=<signature>.
import hmac
import hashlib
def verify_signature(payload, signature, secret):
expected_signature = hmac.new(
secret.encode('utf-8'),
payload,
hashlib.sha256
).hexdigest()
return hmac.compare_digest(f"sha256={expected_signature}", signature)
const crypto = require('crypto');
function verifySignature(payload, signature, secret) {
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
return signature === `sha256=${expectedSignature}`;
}
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"fmt"
)
func verifySignature(payload []byte, signature, secret string) bool {
h := hmac.New(sha256.New, []byte(secret))
h.Write(payload)
expectedSignature := fmt.Sprintf("sha256=%s", hex.EncodeToString(h.Sum(nil)))
return hmac.Equal([]byte(signature), []byte(expectedSignature))
}
Headers
Every request carries these headers:| Header | Description |
|---|---|
Content-Type | Always application/json |
User-Agent | Libredesk-Webhook/<version> |
X-Libredesk-Signature | HMAC signature (if secret is configured) |
SSRF protection
Webhook URLs are one of several admin-configured outbound URLs covered by libredesk’s optional SSRF guard, which is disabled by default. See SSRF protection for details and how to enable it.Events
Conversation events
conversation.created
conversation.created
Triggered when a new conversation is created, by a contact or by an agent.
{
"event": "conversation.created",
"timestamp": "2025-06-15T10:30:00Z",
"payload": {
"id": 123,
"created_at": "2025-06-15T10:30:00Z",
"updated_at": "2025-06-15T10:30:00Z",
"uuid": "550e8400-e29b-41d4-a716-446655440000",
"contact_id": 456,
"inbox_id": 1,
"closed_at": null,
"resolved_at": null,
"last_resolved_at": null,
"contact_last_seen_at": null,
"reference_number": "100",
"priority": "Medium",
"priority_id": 2,
"status": "Open",
"status_category": "open",
"status_id": 1,
"first_reply_at": null,
"last_reply_at": null,
"assigned_user_id": null,
"assigned_team_id": null,
"assignee": null,
"waiting_since": "2025-06-15T10:30:00Z",
"snoozed_until": null,
"subject": "Help with account setup",
"inbox_mail": "support@example.com",
"inbox_reply_to": "",
"inbox_aliases": [],
"inbox_name": "Support",
"inbox_channel": "email",
"tags": [],
"meta": {},
"custom_attributes": {},
"last_message_at": "2025-06-15T10:30:00Z",
"last_message": "I need help setting up my account",
"last_message_sender": "contact",
"last_interaction": "I need help setting up my account",
"last_interaction_at": "2025-06-15T10:30:00Z",
"last_interaction_sender": "contact",
"contact": {
"id": 456,
"created_at": "2025-06-01T09:00:00Z",
"updated_at": "2025-06-15T10:30:00Z",
"first_name": "John",
"last_name": "Doe",
"email": "john.doe@example.com",
"type": "contact",
"availability_status": "offline",
"avatar_url": null,
"phone_number": null,
"phone_number_country_code": null,
"country": null,
"custom_attributes": {},
"enabled": true,
"last_active_at": null,
"last_login_at": null,
"external_user_id": null
},
"sla_policy_id": null,
"sla_policy_name": null,
"applied_sla_id": null,
"next_sla_deadline_at": null,
"first_response_deadline_at": null,
"resolution_deadline_at": null,
"next_response_deadline_at": null,
"next_response_met_at": null,
"last_inbound_at": "2025-06-15T10:30:00Z",
"contact_last_inbound_at": "2025-06-15T10:30:00Z",
"csat_rating": null,
"csat_feedback": null,
"csat_responded_at": null,
"previous_conversations": null
}
}
conversation.status_changed
conversation.status_changed
Triggered when a conversation’s status is updated.
{
"event": "conversation.status_changed",
"timestamp": "2025-06-15T10:35:00Z",
"payload": {
"conversation_uuid": "550e8400-e29b-41d4-a716-446655440000",
"previous_status": "Open",
"new_status": "Resolved",
"snooze_until": "",
"actor_id": 789,
"conversation": {
"id": 123,
"created_at": "2025-06-15T10:30:00Z",
"updated_at": "2025-06-15T10:35:00Z",
"uuid": "550e8400-e29b-41d4-a716-446655440000",
"contact_id": 456,
"inbox_id": 1,
"closed_at": null,
"resolved_at": "2025-06-15T10:35:00Z",
"last_resolved_at": "2025-06-15T10:35:00Z",
"contact_last_seen_at": null,
"reference_number": "100",
"priority": "Medium",
"priority_id": 2,
"status": "Resolved",
"status_category": "resolved",
"status_id": 3,
"first_reply_at": "2025-06-15T10:31:00Z",
"last_reply_at": "2025-06-15T10:31:00Z",
"assigned_user_id": 789,
"assigned_team_id": null,
"assignee": {
"id": 789,
"first_name": "Jane",
"last_name": "Smith",
"avatar_url": null,
"type": "agent",
"availability_status": "online"
},
"waiting_since": null,
"snoozed_until": null,
"subject": "Help with account setup",
"inbox_mail": "support@example.com",
"inbox_reply_to": "",
"inbox_aliases": [],
"inbox_name": "Support",
"inbox_channel": "email",
"tags": [],
"meta": {},
"custom_attributes": {},
"last_message_at": "2025-06-15T10:34:00Z",
"last_message": "Thank you for your help!",
"last_message_sender": "contact",
"last_interaction": "Thank you for your help!",
"last_interaction_at": "2025-06-15T10:34:00Z",
"last_interaction_sender": "contact",
"contact": {
"id": 456,
"created_at": "2025-06-01T09:00:00Z",
"updated_at": "2025-06-15T10:30:00Z",
"first_name": "John",
"last_name": "Doe",
"email": "john.doe@example.com",
"type": "contact",
"availability_status": "offline",
"avatar_url": null,
"phone_number": null,
"phone_number_country_code": null,
"country": null,
"custom_attributes": {},
"enabled": true,
"last_active_at": null,
"last_login_at": null,
"external_user_id": null
},
"sla_policy_id": null,
"sla_policy_name": null,
"applied_sla_id": null,
"next_sla_deadline_at": null,
"first_response_deadline_at": null,
"resolution_deadline_at": null,
"next_response_deadline_at": null,
"next_response_met_at": null,
"last_inbound_at": "2025-06-15T10:34:00Z",
"contact_last_inbound_at": "2025-06-15T10:34:00Z",
"csat_rating": null,
"csat_feedback": null,
"csat_responded_at": null,
"previous_conversations": null
}
}
}
conversation.assigned
conversation.assigned
Triggered when a conversation is assigned to a user. Team assignment does not send this event.
{
"event": "conversation.assigned",
"timestamp": "2025-06-15T10:32:00Z",
"payload": {
"conversation_uuid": "550e8400-e29b-41d4-a716-446655440000",
"assigned_to": 789,
"actor_id": 789,
"conversation": {
"id": 123,
"created_at": "2025-06-15T10:30:00Z",
"updated_at": "2025-06-15T10:32:00Z",
"uuid": "550e8400-e29b-41d4-a716-446655440000",
"contact_id": 456,
"inbox_id": 1,
"closed_at": null,
"resolved_at": null,
"last_resolved_at": null,
"contact_last_seen_at": null,
"reference_number": "100",
"priority": "Medium",
"priority_id": 2,
"status": "Open",
"status_category": "open",
"status_id": 1,
"first_reply_at": null,
"last_reply_at": null,
"assigned_user_id": 789,
"assigned_team_id": null,
"assignee": {
"id": 789,
"first_name": "Jane",
"last_name": "Smith",
"avatar_url": null,
"type": "agent",
"availability_status": "online"
},
"waiting_since": "2025-06-15T10:30:00Z",
"snoozed_until": null,
"subject": "Help with account setup",
"inbox_mail": "support@example.com",
"inbox_reply_to": "",
"inbox_aliases": [],
"inbox_name": "Support",
"inbox_channel": "email",
"tags": [],
"meta": {},
"custom_attributes": {},
"last_message_at": "2025-06-15T10:30:00Z",
"last_message": "I need help setting up my account",
"last_message_sender": "contact",
"last_interaction": "I need help setting up my account",
"last_interaction_at": "2025-06-15T10:30:00Z",
"last_interaction_sender": "contact",
"contact": {
"id": 456,
"created_at": "2025-06-01T09:00:00Z",
"updated_at": "2025-06-15T10:30:00Z",
"first_name": "John",
"last_name": "Doe",
"email": "john.doe@example.com",
"type": "contact",
"availability_status": "offline",
"avatar_url": null,
"phone_number": null,
"phone_number_country_code": null,
"country": null,
"custom_attributes": {},
"enabled": true,
"last_active_at": null,
"last_login_at": null,
"external_user_id": null
},
"sla_policy_id": null,
"sla_policy_name": null,
"applied_sla_id": null,
"next_sla_deadline_at": null,
"first_response_deadline_at": null,
"resolution_deadline_at": null,
"next_response_deadline_at": null,
"next_response_met_at": null,
"last_inbound_at": "2025-06-15T10:30:00Z",
"contact_last_inbound_at": "2025-06-15T10:30:00Z",
"csat_rating": null,
"csat_feedback": null,
"csat_responded_at": null,
"previous_conversations": null
}
}
}
conversation.unassigned
conversation.unassigned
Triggered when a conversation is unassigned from a user.
{
"event": "conversation.unassigned",
"timestamp": "2025-06-15T10:40:00Z",
"payload": {
"conversation_uuid": "550e8400-e29b-41d4-a716-446655440000",
"actor_id": 789,
"conversation": {
"id": 123,
"created_at": "2025-06-15T10:30:00Z",
"updated_at": "2025-06-15T10:40:00Z",
"uuid": "550e8400-e29b-41d4-a716-446655440000",
"contact_id": 456,
"inbox_id": 1,
"closed_at": null,
"resolved_at": null,
"last_resolved_at": null,
"contact_last_seen_at": null,
"reference_number": "100",
"priority": "Medium",
"priority_id": 2,
"status": "Open",
"status_category": "open",
"status_id": 1,
"first_reply_at": "2025-06-15T10:31:00Z",
"last_reply_at": "2025-06-15T10:31:00Z",
"assigned_user_id": null,
"assigned_team_id": null,
"assignee": null,
"waiting_since": "2025-06-15T10:35:00Z",
"snoozed_until": null,
"subject": "Help with account setup",
"inbox_mail": "support@example.com",
"inbox_reply_to": "",
"inbox_aliases": [],
"inbox_name": "Support",
"inbox_channel": "email",
"tags": [],
"meta": {},
"custom_attributes": {},
"last_message_at": "2025-06-15T10:35:00Z",
"last_message": "I still need help",
"last_message_sender": "contact",
"last_interaction": "I still need help",
"last_interaction_at": "2025-06-15T10:35:00Z",
"last_interaction_sender": "contact",
"contact": {
"id": 456,
"created_at": "2025-06-01T09:00:00Z",
"updated_at": "2025-06-15T10:30:00Z",
"first_name": "John",
"last_name": "Doe",
"email": "john.doe@example.com",
"type": "contact",
"availability_status": "offline",
"avatar_url": null,
"phone_number": null,
"phone_number_country_code": null,
"country": null,
"custom_attributes": {},
"enabled": true,
"last_active_at": null,
"last_login_at": null,
"external_user_id": null
},
"sla_policy_id": null,
"sla_policy_name": null,
"applied_sla_id": null,
"next_sla_deadline_at": null,
"first_response_deadline_at": null,
"resolution_deadline_at": null,
"next_response_deadline_at": null,
"next_response_met_at": null,
"last_inbound_at": "2025-06-15T10:35:00Z",
"contact_last_inbound_at": "2025-06-15T10:35:00Z",
"csat_rating": null,
"csat_feedback": null,
"csat_responded_at": null,
"previous_conversations": null
}
}
}
conversation.tags_changed
conversation.tags_changed
Triggered when tags are added or removed from a conversation.
{
"event": "conversation.tags_changed",
"timestamp": "2025-06-15T10:45:00Z",
"payload": {
"conversation_uuid": "550e8400-e29b-41d4-a716-446655440000",
"previous_tags": [
"bug"
],
"new_tags": [
"bug",
"priority"
],
"actor_id": 789,
"conversation": {
"id": 123,
"created_at": "2025-06-15T10:30:00Z",
"updated_at": "2025-06-15T10:45:00Z",
"uuid": "550e8400-e29b-41d4-a716-446655440000",
"contact_id": 456,
"inbox_id": 1,
"closed_at": null,
"resolved_at": null,
"last_resolved_at": null,
"contact_last_seen_at": null,
"reference_number": "100",
"priority": "Medium",
"priority_id": 2,
"status": "Open",
"status_category": "open",
"status_id": 1,
"first_reply_at": "2025-06-15T10:31:00Z",
"last_reply_at": "2025-06-15T10:31:00Z",
"assigned_user_id": 789,
"assigned_team_id": null,
"assignee": {
"id": 789,
"first_name": "Jane",
"last_name": "Smith",
"avatar_url": null,
"type": "agent",
"availability_status": "online"
},
"waiting_since": null,
"snoozed_until": null,
"subject": "Help with account setup",
"inbox_mail": "support@example.com",
"inbox_reply_to": "",
"inbox_aliases": [],
"inbox_name": "Support",
"inbox_channel": "email",
"tags": [
"bug",
"priority"
],
"meta": {},
"custom_attributes": {},
"last_message_at": "2025-06-15T10:30:00Z",
"last_message": "I need help setting up my account",
"last_message_sender": "contact",
"last_interaction": "I need help setting up my account",
"last_interaction_at": "2025-06-15T10:30:00Z",
"last_interaction_sender": "contact",
"contact": {
"id": 456,
"created_at": "2025-06-01T09:00:00Z",
"updated_at": "2025-06-15T10:30:00Z",
"first_name": "John",
"last_name": "Doe",
"email": "john.doe@example.com",
"type": "contact",
"availability_status": "offline",
"avatar_url": null,
"phone_number": null,
"phone_number_country_code": null,
"country": null,
"custom_attributes": {},
"enabled": true,
"last_active_at": null,
"last_login_at": null,
"external_user_id": null
},
"sla_policy_id": null,
"sla_policy_name": null,
"applied_sla_id": null,
"next_sla_deadline_at": null,
"first_response_deadline_at": null,
"resolution_deadline_at": null,
"next_response_deadline_at": null,
"next_response_met_at": null,
"last_inbound_at": "2025-06-15T10:30:00Z",
"contact_last_inbound_at": "2025-06-15T10:30:00Z",
"csat_rating": null,
"csat_feedback": null,
"csat_responded_at": null,
"previous_conversations": null
}
}
}
Message events
message.created
message.created
Triggered when a new message is created in a conversation. This includes incoming and outgoing messages, private notes, and activity messages.
{
"event": "message.created",
"timestamp": "2025-06-15T10:33:00Z",
"payload": {
"id": 987,
"created_at": "2025-06-15T10:33:00Z",
"updated_at": "2025-06-15T10:33:00Z",
"uuid": "123e4567-e89b-12d3-a456-426614174000",
"type": "outgoing",
"status": "pending",
"conversation_id": 123,
"conversation_uuid": "550e8400-e29b-41d4-a716-446655440000",
"content": "<p>Hello! How can I help you today?</p>",
"text_content": "Hello! How can I help you today?",
"content_type": "html",
"private": false,
"sender_id": 789,
"sender_type": "agent",
"meta": {},
"attachments": [],
"author": {
"id": 789,
"first_name": "Jane",
"last_name": "Smith",
"email": "jane.smith@example.com",
"avatar_url": null,
"availability_status": "online",
"type": "agent",
"last_active_at": "2025-06-15T10:33:00Z"
}
}
}
message.updated
message.updated
Triggered when an outgoing message’s status changes to
sent or failed.{
"event": "message.updated",
"timestamp": "2025-06-15T10:33:02Z",
"payload": {
"id": 987,
"created_at": "2025-06-15T10:33:00Z",
"updated_at": "2025-06-15T10:33:02Z",
"uuid": "123e4567-e89b-12d3-a456-426614174000",
"type": "outgoing",
"status": "sent",
"conversation_id": 123,
"conversation_uuid": "550e8400-e29b-41d4-a716-446655440000",
"content": "<p>Hello! How can I help you today?</p>",
"text_content": "Hello! How can I help you today?",
"content_type": "html",
"private": false,
"sender_id": 789,
"sender_type": "agent",
"meta": {},
"attachments": [],
"author": {
"id": 789,
"first_name": "Jane",
"last_name": "Smith",
"email": "jane.smith@example.com",
"avatar_url": null,
"availability_status": "online",
"type": "agent",
"last_active_at": "2025-06-15T10:33:00Z"
}
}
}
Automation rules
The Trigger webhook automation action sends a custom event, for examplepriority.escalated, to one active webhook.
{
"event": "priority.escalated",
"timestamp": "2025-06-15T10:50:00Z",
"payload": {
"conversation": { "id": 123, "uuid": "550e8400-e29b-41d4-a716-446655440000" },
"actor_id": 1
}
}
conversation is shortened here.
Delivery and retries
- Deliveries run in a background worker pool.
- Failed deliveries are not retried.
- Events are dropped when the delivery queue is full.
[webhook] section of config.toml.
Because there are no retries, verify the signature and reply within 3 seconds, then do the actual work after you have responded. An endpoint that blocks on slow processing will time out and lose the event.
Testing
Both of these give you a throwaway URL that shows the payloads libredesk sends:Webhook.site
Generate a temporary URL to inspect webhook payloads
RequestBin
Collect and inspect HTTP requests
Example integration
An Express server that verifies the signature, replies immediately, and processes the event afterwards:const express = require('express');
const crypto = require('crypto');
const app = express();
// Middleware to capture raw body for signature verification
app.use(express.raw({ type: 'application/json' }));
function verifySignature(payload, signature, secret) {
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
return signature === `sha256=${expectedSignature}`;
}
app.post('/webhook', (req, res) => {
const signature = req.headers['x-libredesk-signature'];
const secret = process.env.WEBHOOK_SECRET;
// Verify signature
if (secret && !verifySignature(req.body, signature, secret)) {
return res.status(401).send('Invalid signature');
}
const event = JSON.parse(req.body);
// Process event asynchronously
setImmediate(() => {
processWebhookEvent(event);
});
// Respond immediately
res.status(200).send('OK');
});
function processWebhookEvent(event) {
console.log(`Received event: ${event.event}`);
switch(event.event) {
case 'conversation.created':
// Handle new conversation
break;
case 'message.created':
// Handle new message
break;
// Add more cases as needed
}
}
app.listen(3000, () => {
console.log('Webhook server listening on port 3000');
});

