Skip to main content
Search

Webhooks

Receive real-time HTTP notifications for Libredesk events

When something happens in your libredesk instance, libredesk sends an HTTP POST to a URL you choose. The body is JSON containing the event name, a timestamp, and the conversation or message it happened to. Use webhooks to push libredesk activity into your own systems.

Configuration

Navigate to webhooks

Go to Admin → Integrations → Webhooks in your libredesk dashboard

Create webhook

Click Create 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
  • Status: enabled or disabled

Test webhook

Use the test button to send a sample payload to verify your endpoint is working

Security

Signature verification

When you set a secret, libredesk signs the payload with HMAC-SHA256 and sends the result in the X-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

Triggered when a new conversation is created.

{
  "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,
    "reference_number": "100",
    "priority": "Medium",
    "priority_id": 2,
    "status": "Open",
    "status_id": 1,
    "subject": "Help with account setup",
    "inbox_name": "Support",
    "inbox_channel": "email",
    "contact": {
      "id": 456,
      "first_name": "John",
      "last_name": "Doe",
      "email": "[email protected]",
      "type": "contact"
    },
    "custom_attributes": {},
    "tags": []
  }
}

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",
      "reference_number": "100",
      "priority": "Medium",
      "priority_id": 2,
      "status": "Resolved",
      "status_id": 3,
      "first_reply_at": "2025-06-15T10:31:00Z",
      "last_reply_at": "2025-06-15T10:34:00Z",
      "assigned_user_id": 789,
      "assigned_team_id": null,
      "waiting_since": null,
      "subject": "Help with account setup",
      "inbox_mail": "[email protected]",
      "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": "[email protected]",
        "type": "contact",
        "availability_status": "offline",
        "avatar_url": null,
        "phone_number": null,
        "phone_number_country_code": null,
        "custom_attributes": {},
        "enabled": true,
        "last_active_at": null,
        "last_login_at": null
      },
      "sla_policy_id": null,
      "sla_policy_name": null,
      "applied_sla_id": null,
      "first_response_deadline_at": null,
      "resolution_deadline_at": null,
      "next_response_deadline_at": null,
      "next_response_met_at": null,
      "previous_conversations": null
    }
  }
}

conversation.assigned

Triggered when a conversation is assigned to a user.

{
  "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,
      "reference_number": "100",
      "priority": "Medium",
      "priority_id": 2,
      "status": "Open",
      "status_id": 1,
      "first_reply_at": null,
      "last_reply_at": null,
      "assigned_user_id": 789,
      "assigned_team_id": null,
      "waiting_since": "2025-06-15T10:30:00Z",
      "subject": "Help with account setup",
      "inbox_mail": "[email protected]",
      "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": "[email protected]",
        "type": "contact",
        "availability_status": "offline",
        "avatar_url": null,
        "phone_number": null,
        "phone_number_country_code": null,
        "custom_attributes": {},
        "enabled": true,
        "last_active_at": null,
        "last_login_at": null
      },
      "sla_policy_id": null,
      "sla_policy_name": null,
      "applied_sla_id": null,
      "first_response_deadline_at": null,
      "resolution_deadline_at": null,
      "next_response_deadline_at": null,
      "next_response_met_at": null,
      "previous_conversations": null
    }
  }
}

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,
      "reference_number": "100",
      "priority": "Medium",
      "priority_id": 2,
      "status": "Open",
      "status_id": 1,
      "first_reply_at": "2025-06-15T10:31:00Z",
      "last_reply_at": "2025-06-15T10:35:00Z",
      "assigned_user_id": null,
      "assigned_team_id": null,
      "waiting_since": "2025-06-15T10:35:00Z",
      "subject": "Help with account setup",
      "inbox_mail": "[email protected]",
      "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": "[email protected]",
        "type": "contact",
        "availability_status": "offline",
        "avatar_url": null,
        "phone_number": null,
        "phone_number_country_code": null,
        "custom_attributes": {},
        "enabled": true,
        "last_active_at": null,
        "last_login_at": null
      },
      "sla_policy_id": null,
      "sla_policy_name": null,
      "applied_sla_id": null,
      "first_response_deadline_at": null,
      "resolution_deadline_at": null,
      "next_response_deadline_at": null,
      "next_response_met_at": null,
      "previous_conversations": null
    }
  }
}

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,
      "reference_number": "100",
      "priority": "Medium",
      "priority_id": 2,
      "status": "Open",
      "status_id": 1,
      "first_reply_at": "2025-06-15T10:31:00Z",
      "last_reply_at": "2025-06-15T10:35:00Z",
      "assigned_user_id": 789,
      "assigned_team_id": null,
      "waiting_since": null,
      "subject": "Help with account setup",
      "inbox_mail": "[email protected]",
      "inbox_name": "Support",
      "inbox_channel": "email",
      "tags": ["bug", "priority"],
      "meta": [],
      "custom_attributes": {},
      "last_message_at": "2025-06-15T10:35: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: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": "[email protected]",
        "type": "contact",
        "availability_status": "offline",
        "avatar_url": null,
        "phone_number": null,
        "phone_number_country_code": null,
        "custom_attributes": {},
        "enabled": true,
        "last_active_at": null,
        "last_login_at": null
      },
      "sla_policy_id": null,
      "sla_policy_name": null,
      "applied_sla_id": null,
      "first_response_deadline_at": null,
      "resolution_deadline_at": null,
      "next_response_deadline_at": null,
      "next_response_met_at": null,
      "previous_conversations": null
    }
  }
}

Message events

message.created

Triggered when a new message is created in a conversation.

{
  "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": "sent",
    "conversation_id": 123,
    "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",
    "attachments": []
  }
}

message.updated

Triggered when an existing message is updated.

{
  "event": "message.updated",
  "timestamp": "2025-06-15T10:34:00Z",
  "payload": {
    "id": 987,
    "created_at": "2025-06-15T10:33:00Z",
    "updated_at": "2025-06-15T10:34:00Z",
    "uuid": "123e4567-e89b-12d3-a456-426614174000",
    "type": "outgoing",
    "status": "sent",
    "conversation_id": 123,
    "content": "<p>Hello! How can I help you today? (Updated)</p>",
    "text_content": "Hello! How can I help you today? (Updated)",
    "content_type": "html",
    "private": false,
    "sender_id": 789,
    "sender_type": "agent",
    "attachments": []
  }
}

Delivery and retries

  • Deliveries run in a background worker pool.
  • The request timeout is set in config.toml.
  • Failed deliveries are not retried.
  • Events can be dropped when the delivery queue is full. The queue size is set in config.toml.

Because there are no retries, verify the signature and reply within 5 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');
});
Was this article helpful?