Corpland Desk

Webhooks

Signed events posted to your server, with retries and a delivery log.

Add an endpoint in Settings, Webhooks, pick the events you want and copy its signing secret (whsec_...).

Events

EventSent when
conversation.createdA visitor or your API starts a conversation.
conversation.assignedSomeone is assigned or unassigned.
conversation.resolvedA conversation is resolved.
message.createdA public message is sent by either side. Internal notes are never sent.
csat.receivedA visitor rates a resolved conversation.
ticket.createdA bug report or request is filed.
ticket.updatedStatus, priority, type or assignee changes.
ticket.commentedSomeone comments on a ticket.
feedback.createdFeedback or a rating arrives.
feedback.updatedFeedback status, reply or tags change.
contact.updatedA contact is created or their details change.

The request

POST /your/endpoint
Content-Type: application/json
User-Agent: CorplandDesk-Webhooks/1
Desk-Event: ticket.created
Desk-Delivery: 6aca9f36b70fd74072119f84
Desk-Signature: t=1791664596,v1=5f1c...
{
  "id": "evt_...",
  "type": "ticket.created",
  "created": 1791664596,
  "workspace_id": "6aca9f36b70fd74072119f84",
  "data": { "_id": "...", "number": 12, "type": "bug", "title": "Export stuck at 99%" }
}

Verify the signature

The signature is HMAC_SHA256(secret, "<t>.<raw body>") as hex. Use the raw body exactly as received, before any JSON parsing, and reject timestamps older than five minutes.

Express
import crypto from "node:crypto";
import express from "express";

const app = express();

app.post("/webhooks/desk", express.raw({ type: "application/json" }), (req, res) => {
  const header = String(req.header("Desk-Signature") || "");
  const parts = Object.fromEntries(header.split(",").map((part) => part.split("=")));
  const expected = crypto.createHmac("sha256", process.env.DESK_WEBHOOK_SECRET!).update(`${parts.t}.${req.body}`).digest("hex");
  const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) < 300;
  const valid = parts.v1 && expected.length === parts.v1.length && crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1));
  if (!fresh || !valid) return res.status(400).send("bad signature");
  const event = JSON.parse(req.body.toString());
  res.sendStatus(200);
  handle(event);
});
Python
import hmac, hashlib, time

def verify(secret: str, header: str, raw_body: bytes) -> bool:
    parts = dict(p.split("=", 1) for p in header.split(","))
    expected = hmac.new(secret.encode(), f"{parts['t']}.".encode() + raw_body, hashlib.sha256).hexdigest()
    return abs(time.time() - int(parts["t"])) < 300 and hmac.compare_digest(expected, parts.get("v1", ""))

Retries

Answer with any 2xx within 10 seconds. Anything else is retried after 1 minute, 5 minutes, 30 minutes, 2 hours and 12 hours, six tries in all. Desk-Delivery stays the same across retries, so use it to ignore duplicates.

Every attempt is in the delivery log under the endpoint in Settings, Webhooks, with the status code and the first 500 characters of your response. Failed deliveries can be sent again from there, and Send a test posts a ping event.

In production, endpoints must use https and resolve to a public address.

On this page