Webhooks

Send real-time HTTPS notifications to your endpoint: create webhooks in the admin or API, all 36 events, payloads, signature verification, and retry behavior.

JM
James Morton
Written By James MortonLast updated 1 day ago

Webhooks send an HTTPS POST to your endpoint when something happens in your workspace, such as a new post, a status change, a new conversation or a ticket reply. Use them to connect custom bots, data warehouses, internal tools, or automation platforms like Zapier and n8n. This article covers creating webhooks, the event list, payloads, signature verification and delivery behavior.

Note: On Quackback Cloud, webhooks are included from the Pro plan. Each workspace can have up to 25 webhooks.

Create a webhook in the admin

  1. Go to Admin > Settings > Developers and open the Webhooks tab.
  2. Click Create webhook.
  3. Enter the Endpoint URL. It must use HTTPS.
  4. Under Events, select at least one event.
  5. Click Create webhook.
  6. Copy the Signing secret now. It is shown only once.

From the list you can edit a webhook's URL and events, turn it off (an off webhook receives no events), rotate its secret, or delete it. Turning a webhook back on resets its failure count.

Create a webhook with the API

You can also manage webhooks through the REST API with an API key:

curl -X POST https://feedback.example.com/api/v1/webhooks \
  -H "Authorization: Bearer qb_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://your-server.com/webhook",
    "events": ["post.created", "post.status_changed"],
    "boardIds": ["board_01h455vb4pex5vsknk084sn02q"]
  }'

The response includes the signing secret (whsec_...), shown only once. Other endpoints:

Method

Path

Does

GET

/api/v1/webhooks

List webhooks

GET

/api/v1/webhooks/{id}

Get one webhook

PATCH

/api/v1/webhooks/{id}

Update url, events, boardIds, or status (active or disabled)

POST

/api/v1/webhooks/{id}/rotate

Rotate the signing secret (the response contains the new one)

DELETE

/api/v1/webhooks/{id}

Delete the webhook

boardIds limits post and comment events to specific boards. Leave it out to receive events from all boards. Conversation, message and ticket events aren't tied to a board, so they are always delivered for the events you subscribed to.

Events

Posts

Event

Sent when

post.created

New feedback is submitted

post.status_changed

A post's status changes

post.updated

A post's title, content, tags or owner changes

post.deleted

A post is deleted

post.restored

A deleted post is restored

post.merged

A duplicate is merged into another post

post.unmerged

A merged post is separated again

post.voted

Someone votes on a post

Comments

Event

Sent when

comment.created

A new comment is posted

comment.updated

A comment is edited

comment.deleted

A comment is deleted

Note: Private comments never trigger a webhook.

Changelog

Event

Sent when

changelog.published

A changelog entry is published

Conversations

Event

Sent when

conversation.created

A customer starts a conversation

conversation.status_changed

A conversation moves between open, snoozed and closed

conversation.assigned

A conversation is assigned or unassigned (including auto-routing)

conversation.priority_changed

A conversation's priority changes

conversation.attribute_changed

A conversation attribute is set or cleared by AI, a teammate or a customer

conversation.csat_submitted

A customer submits a satisfaction rating

conversation.csat_comment_added

A customer adds a comment to their rating

conversation.note_mentioned

An internal note @-mentions a teammate

conversation.customer_unresponsive

The customer has been silent for a workflow-configured time

conversation.teammate_unresponsive

No teammate has replied for a workflow-configured time

Messages

Event

Sent when

message.created

A customer or teammate sends a public message

message.note_created

A teammate adds an internal note

message.updated

The author edits a public message

message.deleted

A public message is deleted

Note: Conversation and message events need the Support module. System messages (such as "chat ended") arrive as conversation.* events, not message.created.

Tickets

Event

Sent when

ticket.created

A ticket is opened

ticket.status_changed

A ticket moves between open, pending and closed

ticket.assigned

A ticket is assigned to or unassigned from a teammate or team

ticket.replied

A teammate or the requester replies on the ticket

ticket.note_added

A teammate adds an internal note to a ticket

ticket.external_status_changed

A linked issue in an issue tracker changes status

Quinn and SLAs

Event

Sent when

assistant.handed_off

Quinn hands a conversation to your team

assistant.resolved

Quinn settles a conversation without a handoff

sla.approaching_breach

An SLA clock enters its warning window

sla.breached

An SLA clock passes its due time

Note: Anonymous visitors' placeholder emails are never included in a payload.

Request headers

Header

Value

Content-Type

application/json

User-Agent

Quackback-Webhook/1.0 (+https://quackback.io)

X-Quackback-Event

The event type, for example post.created

X-Quackback-Signature

sha256= followed by the HMAC-SHA256 signature

X-Quackback-Timestamp

Unix timestamp (seconds) of the delivery

Payload

Every delivery sends a JSON body with this shape:

{
  "id": "evt_a1b2c3d4e5f6...",
  "type": "post.created",
  "createdAt": "2026-01-15T10:30:00.000Z",
  "data": {
    "post": {
      "id": "post_01h455vb4pex5vsknk084sn02q",
      "title": "Add dark mode support",
      "content": "It would be great to have a dark mode...",
      "boardId": "board_01h455vb4pex5vsknk084sn02q",
      "boardSlug": "feature-requests",
      "authorEmail": "[email protected]",
      "voteCount": 12
    }
  }
}

data holds objects named after the event family:

Object

Key fields

post (on post.created)

id, title, content, boardId, boardSlug, authorEmail, authorName, voteCount

post (other post events)

id, title, boardId, boardSlug

comment

id, content, authorEmail, authorName, isPrivate

changelog

id, title, contentPreview, publishedAt, linkedPostCount

conversation

id, status, channel, priority, assignedTeamId

message

id, conversationId, senderType, authorPrincipalId, authorName, authorEmail, content, createdAt

ticket

id, number, type, priority, assignedPrincipalId, assignedTeamId

Status-change events add previousStatus and newStatus (or previousPriority and newPriority). Assignment events add the new and previous assignee ids. ticket.replied and ticket.note_added also carry messageId, content, senderType and an attachments array.

Verify the signature

Every delivery is signed with HMAC-SHA256 over {timestamp}.{raw body} using your signing secret. Always verify it, using the raw request body exactly as received:

import crypto from 'crypto';
import express from 'express';

const app = express();

app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
  const signature = req.header('x-quackback-signature');
  const timestamp = req.header('x-quackback-timestamp');
  const rawBody = req.body.toString('utf8');

  const expected = 'sha256=' + crypto
    .createHmac('sha256', process.env.WEBHOOK_SECRET)
    .update(`${timestamp}.${rawBody}`)
    .digest('hex');

  const valid =
    signature &&
    signature.length === expected.length &&
    crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
  const fresh = Math.abs(Date.now() / 1000 - Number(timestamp)) < 300;

  if (!valid || !fresh) return res.status(401).send('Invalid signature');

  const event = JSON.parse(rawBody);
  // Handle event.type here, then respond quickly
  res.status(200).send('OK');
});

The admin's Webhooks tab also shows a verification guide with examples for other frameworks.

Delivery and retries

  • Each delivery attempt times out after 5 seconds. Respond with a 2xx quickly and do slow work afterwards.
  • Server errors (5xx), rate limits (429) and timeouts are retried: twice within a few seconds, then after about 1, 2 and 4 hours. That is up to 6 attempts over roughly 7 hours.
  • Other client errors (4xx) are not retried.
  • After 50 consecutive permanent failures, the webhook is turned off automatically. A successful delivery resets the count.

Best practices

  1. Always verify signatures and reject requests older than 5 minutes.
  2. Use HTTPS endpoints only (required).
  3. Rotate the secret if it may have leaked.
  4. Respond within a few seconds and process events in the background.
  5. Use the event id to ignore duplicates if your handler might see an event twice.

Was this helpful?

Your feedback shapes what we write next.