Webhooks

Webhooks let you receive real-time HTTP POST notifications when events occur in your project. Configure webhook endpoints in the dashboard under Project → Webhooks.

Available events

EventFired when
ticket.createdA new ticket is submitted (via portal, API, or SDK)
ticket.resolvedA ticket is marked as resolved
message.receivedA new message is added to a ticket

Payload format

Each webhook delivery sends a JSON POST request with the following structure:

{
  "event": "ticket.created",
  "timestamp": "2025-01-15T10:30:00.000Z",
  "data": {
    "ticket": {
      "id": "uuid",
      "number": 42,
      "subject": "Can't reset password",
      "status": "open",
      "priority": "high"
    },
    "customer": {
      "email": "user@example.com",
      "name": "Jane Smith"
    }
  }
}

Headers

Every webhook delivery includes these headers:

HeaderDescription
Content-TypeAlways application/json
X-Helmdesk-EventThe event type (e.g. ticket.created)
X-Helmdesk-SignatureHMAC-SHA256 signature of the request body

Verifying signatures

Every delivery is signed with your webhook secret using HMAC-SHA256. Always verify the signature to ensure the request came from Helmdesk.

import { createHmac } from 'crypto'

function verifyWebhookSignature(
  body: string,
  signature: string,
  secret: string
): boolean {
  const expected = createHmac('sha256', secret)
    .update(body)
    .digest('hex')
  return signature === expected
}

// Express / Next.js API route example
export async function POST(req: Request) {
  const body = await req.text()
  const signature = req.headers.get('x-helmdesk-signature') ?? ''

  if (!verifyWebhookSignature(body, signature, WEBHOOK_SECRET)) {
    return new Response('Invalid signature', { status: 401 })
  }

  const payload = JSON.parse(body)

  switch (payload.event) {
    case 'ticket.created':
      // Handle new ticket
      break
    case 'ticket.resolved':
      // Handle resolved ticket
      break
    case 'message.received':
      // Handle new message
      break
  }

  return new Response('OK', { status: 200 })
}

Slack integration

Enable Slack format when creating a webhook to receive payloads formatted as Slack Block Kit messages. Point the webhook URL at a Slack incoming webhook to get formatted notifications in your channel.

// Slack-formatted payload example
{
  "blocks": [
    {
      "type": "section",
      "text": {
        "type": "mrkdwn",
        "text": "*New Ticket Created*\n> *Subject:* Can't reset password\n> *#42*\n> *Customer:* user@example.com\n> *Priority:* high"
      }
    }
  ]
}

Delivery behavior

  • • Webhooks are delivered asynchronously (fire-and-forget) and do not block API responses
  • • Deliveries time out after 5 seconds
  • • All deliveries (successes and failures) are logged and visible in the dashboard
  • • Use the Test button in the dashboard to send a sample delivery to your endpoint