Webhooks

In this guide, we will look at how to register and consume webhooks to integrate your app with the ITSM platform. With webhooks, your app can react in real time when something happens — such as a ticket being created, an SLA breach, a triage completing, or a change request moving through approval.

The webhook model

The webhook model contains all the information about a registered webhook endpoint, including the target URL, subscribed event types, and its current status.

Properties

  • Name
    id
    Type
    string
    Description

    Unique identifier for the webhook. Prefixed with whk_.

  • Name
    url
    Type
    string
    Description

    The URL that ITSM will send webhook events to. Must be HTTPS.

  • Name
    name
    Type
    string
    Description

    A human-readable name for the webhook.

  • Name
    events
    Type
    array
    Description

    An array of event types this webhook is subscribed to. Use ["*"] to subscribe to all events.

  • Name
    secret
    Type
    string
    Description

    The secret key used to generate HMAC-SHA256 signatures. Only returned on creation.

  • Name
    active
    Type
    boolean
    Description

    Whether the webhook is currently enabled and receiving events.

  • Name
    organization_id
    Type
    string
    Description

    The organization this webhook belongs to.

  • Name
    created_at
    Type
    timestamp
    Description

    Timestamp of when the webhook was registered.

  • Name
    updated_at
    Type
    timestamp
    Description

    Timestamp of when the webhook was last updated.


GET/v1/webhooks

List all webhooks

This endpoint allows you to retrieve a paginated list of all webhooks registered for your organization.

Optional attributes

  • Name
    active
    Type
    boolean
    Description

    Filter webhooks by active status.

  • Name
    limit
    Type
    integer
    Description

    Limit the number of webhooks returned. Defaults to 10.

Request

GET
/v1/webhooks
curl -G https://api.itsm.example/v1/webhooks \
  -H "Authorization: Bearer {token}" \
  -d active=true \
  -d limit=10

Response

{
  "has_more": false,
  "data": [
    {
      "id": "whk_abc123",
      "url": "https://myapp.example/webhooks/itsm",
      "name": "Production ticket alerts",
      "events": ["ticket.created", "ticket.resolved", "sla.breached"],
      "active": true,
      "organization_id": "org_xyz",
      "created_at": "2026-02-20T08:00:00Z",
      "updated_at": "2026-02-20T08:00:00Z"
    },
    {
      "id": "whk_def456"
      // ...
    }
  ]
}

POST/v1/webhooks

Create a webhook

This endpoint allows you to register a new webhook for your organization. You must provide a target URL and at least one event type to subscribe to. The response will include a secret field — store this securely, as it will not be shown again.

Required attributes

  • Name
    url
    Type
    string
    Description

    The HTTPS URL to deliver webhook events to.

  • Name
    events
    Type
    array
    Description

    An array of event types to subscribe to. Use ["*"] for all events.

Optional attributes

  • Name
    name
    Type
    string
    Description

    A human-readable name for the webhook.

  • Name
    active
    Type
    boolean
    Description

    Whether the webhook should be active immediately. Defaults to true.

Request

POST
/v1/webhooks
curl https://api.itsm.example/v1/webhooks \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://myapp.example/webhooks/itsm",
    "name": "Production ticket alerts",
    "events": ["ticket.created", "ticket.resolved", "sla.breached"]
  }'

Response

{
  "id": "whk_abc123",
  "url": "https://myapp.example/webhooks/itsm",
  "name": "Production ticket alerts",
  "events": ["ticket.created", "ticket.resolved", "sla.breached"],
  "secret": "whsec_5f3e8a1b9c2d4e6f7a8b9c0d1e2f3a4b",
  "active": true,
  "organization_id": "org_xyz",
  "created_at": "2026-02-24T10:00:00Z",
  "updated_at": "2026-02-24T10:00:00Z"
}

GET/v1/webhooks/:id

Retrieve a webhook

This endpoint allows you to retrieve a webhook by its ID. The secret field is not included in the response — it is only returned at creation time.

Request

GET
/v1/webhooks/whk_abc123
curl https://api.itsm.example/v1/webhooks/whk_abc123 \
  -H "Authorization: Bearer {token}"

Response

{
  "id": "whk_abc123",
  "url": "https://myapp.example/webhooks/itsm",
  "name": "Production ticket alerts",
  "events": ["ticket.created", "ticket.resolved", "sla.breached"],
  "active": true,
  "organization_id": "org_xyz",
  "created_at": "2026-02-20T08:00:00Z",
  "updated_at": "2026-02-20T08:00:00Z"
}

PUT/v1/webhooks/:id

Update a webhook

This endpoint allows you to update an existing webhook configuration. You can change the URL, subscribed events, name, or toggle the active state. Updating a webhook does not regenerate the secret.

Optional attributes

  • Name
    url
    Type
    string
    Description

    The HTTPS URL to deliver webhook events to.

  • Name
    name
    Type
    string
    Description

    A human-readable name for the webhook.

  • Name
    events
    Type
    array
    Description

    An array of event types to subscribe to. Replaces the existing list.

  • Name
    active
    Type
    boolean
    Description

    Whether the webhook is enabled.

Request

PUT
/v1/webhooks/whk_abc123
curl -X PUT https://api.itsm.example/v1/webhooks/whk_abc123 \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -d '{
    "events": ["ticket.created", "ticket.resolved", "sla.breached", "triage.completed"],
    "name": "Production alerts (expanded)"
  }'

Response

{
  "id": "whk_abc123",
  "url": "https://myapp.example/webhooks/itsm",
  "name": "Production alerts (expanded)",
  "events": ["ticket.created", "ticket.resolved", "sla.breached", "triage.completed"],
  "active": true,
  "organization_id": "org_xyz",
  "created_at": "2026-02-20T08:00:00Z",
  "updated_at": "2026-02-24T10:30:00Z"
}

DELETE/v1/webhooks/:id

Delete a webhook

This endpoint allows you to delete a webhook. Once deleted, no further events will be delivered to the webhook URL. Any pending deliveries will be cancelled.

Request

DELETE
/v1/webhooks/whk_abc123
curl -X DELETE https://api.itsm.example/v1/webhooks/whk_abc123 \
  -H "Authorization: Bearer {token}"

POST/v1/webhooks/:id/test

Test a webhook

This endpoint sends a test event to your webhook URL so you can verify that your endpoint is correctly configured and can process deliveries. The test event uses the webhook.test event type with a sample payload.

Request

POST
/v1/webhooks/whk_abc123/test
curl -X POST https://api.itsm.example/v1/webhooks/whk_abc123/test \
  -H "Authorization: Bearer {token}"

Response

{
  "id": "dlv_test_001",
  "webhook_id": "whk_abc123",
  "event_type": "webhook.test",
  "status": "delivered",
  "response_code": 200,
  "response_time_ms": 142,
  "delivered_at": "2026-02-24T10:05:00Z"
}

GET/v1/webhooks/:id/deliveries

Delivery history

This endpoint returns the delivery history for a specific webhook. Each delivery record includes the event type, HTTP response code, response time, and whether the delivery was successful. Use this to diagnose failed deliveries and monitor webhook health.

Optional attributes

  • Name
    status
    Type
    string
    Description

    Filter by delivery status: delivered, failed, or pending.

  • Name
    event_type
    Type
    string
    Description

    Filter by event type, e.g. ticket.created.

  • Name
    limit
    Type
    integer
    Description

    Limit the number of deliveries returned. Defaults to 20.

Request

GET
/v1/webhooks/whk_abc123/deliveries
curl -G https://api.itsm.example/v1/webhooks/whk_abc123/deliveries \
  -H "Authorization: Bearer {token}" \
  -d status=failed \
  -d limit=5

Response

{
  "has_more": true,
  "data": [
    {
      "id": "dlv_789xyz",
      "webhook_id": "whk_abc123",
      "event_id": "evt_abc123",
      "event_type": "ticket.created",
      "status": "failed",
      "response_code": 500,
      "response_time_ms": 3021,
      "attempt": 3,
      "max_attempts": 3,
      "next_retry_at": null,
      "delivered_at": null,
      "failed_at": "2026-02-24T10:02:45Z",
      "created_at": "2026-02-24T10:00:00Z"
    }
  ]
}

Consuming webhooks

When your app receives a webhook request from ITSM, check the type attribute to determine what event occurred. The first part of the event type indicates the resource category (e.g., ticket, triage, sla), and the second part describes the action.

Example webhook payload

{
  "id": "evt_abc123",
  "type": "ticket.created",
  "timestamp": "2026-02-24T10:00:00Z",
  "organizationId": "org_xyz",
  "payload": {
    "id": "tkt_123",
    "title": "VPN connection issue",
    "status": "new",
    "priority": "high",
    "type": "incident"
  }
}

Your endpoint should return a 2xx status code within 30 seconds to acknowledge receipt. If your endpoint returns a non-2xx response or times out, ITSM will retry the delivery according to the retry behavior described below.


Event types

Ticket events

  • Name
    ticket.created
    Description

    A new ticket was created, either manually or through an integration.

  • Name
    ticket.updated
    Description

    A ticket fields were updated (title, description, priority, type, or custom fields).

  • Name
    ticket.status_changed
    Description

    A ticket status transitioned (e.g., new to in_progress, in_progress to resolved).

  • Name
    ticket.assigned
    Description

    A ticket was assigned or reassigned to an agent or team.

  • Name
    ticket.resolved
    Description

    A ticket was marked as resolved.

  • Name
    ticket.closed
    Description

    A ticket was closed (final state after resolution confirmation).

Triage events

  • Name
    triage.completed
    Description

    AI triage finished classifying a ticket with priority, category, and routing recommendation.

  • Name
    triage.auto_resolved
    Description

    AI triage determined the ticket could be auto-resolved and applied a resolution.

  • Name
    triage.escalated
    Description

    AI triage escalated a ticket to a human agent due to complexity or sensitivity.

Workflow events

  • Name
    workflow.started
    Description

    An automated workflow (Temporal) was started for a ticket or change request.

  • Name
    workflow.completed
    Description

    A workflow completed all steps successfully.

  • Name
    workflow.failed
    Description

    A workflow encountered an unrecoverable error.

SLA events

  • Name
    sla.warning
    Description

    A ticket is approaching its SLA deadline (configurable warning threshold).

  • Name
    sla.breached
    Description

    A ticket has exceeded its SLA response or resolution time.

Comment events

  • Name
    comment.added
    Description

    A comment was added to a ticket, either by an agent, customer, or the system.

Change management events

  • Name
    change.submitted
    Description

    A change request was submitted for review.

  • Name
    change.approved
    Description

    A change request was approved by the required approvers.

  • Name
    change.rejected
    Description

    A change request was rejected.

  • Name
    change.implemented
    Description

    A change request was successfully implemented in production.

Connector events

  • Name
    connector.synced
    Description

    An external connector (e.g., ServiceNow, Jira) completed a sync cycle successfully.

  • Name
    connector.error
    Description

    An external connector encountered an error during sync.

User events

  • Name
    user.created
    Description

    A new user was provisioned in the organization.

  • Name
    user.updated
    Description

    A user profile, role, or permissions were updated.

ticket.created

{
  "id": "evt_abc123",
  "type": "ticket.created",
  "timestamp": "2026-02-24T10:00:00Z",
  "organizationId": "org_xyz",
  "payload": {
    "id": "tkt_123",
    "title": "VPN connection issue",
    "status": "new",
    "priority": "high",
    "type": "incident"
  }
}

triage.completed

{
  "id": "evt_def456",
  "type": "triage.completed",
  "timestamp": "2026-02-24T10:00:05Z",
  "organizationId": "org_xyz",
  "payload": {
    "ticket_id": "tkt_123",
    "priority": "high",
    "category": "network",
    "subcategory": "vpn",
    "confidence": 0.94,
    "routing": {
      "team": "network-ops",
      "agent": "agent_891"
    },
    "model": "hpe-gpt-120b",
    "processing_time_ms": 1240
  }
}

sla.breached

{
  "id": "evt_ghi789",
  "type": "sla.breached",
  "timestamp": "2026-02-24T14:00:00Z",
  "organizationId": "org_xyz",
  "payload": {
    "ticket_id": "tkt_123",
    "sla_policy": "P1 - Critical",
    "metric": "resolution_time",
    "target_minutes": 240,
    "elapsed_minutes": 245,
    "breach_severity": "minor"
  }
}

change.approved

{
  "id": "evt_jkl012",
  "type": "change.approved",
  "timestamp": "2026-02-24T16:30:00Z",
  "organizationId": "org_xyz",
  "payload": {
    "id": "chg_456",
    "title": "Upgrade VPN gateway firmware",
    "risk_level": "medium",
    "approved_by": ["user_111", "user_222"],
    "scheduled_at": "2026-02-25T02:00:00Z"
  }
}

Security

To verify that a webhook was sent by ITSM and not a malicious actor, every webhook request includes an x-itsm-signature header. This signature is an HMAC-SHA256 hash of the raw request body, computed using the webhook secret that was returned when you created the webhook. Always verify this signature before processing the event.

Verifying a request

const crypto = require('crypto')

const signature = req.headers['x-itsm-signature']
const payload = JSON.stringify(req.body)
const hash = crypto
  .createHmac('sha256', secret)
  .update(payload)
  .digest('hex')

if (crypto.timingSafeEqual(Buffer.from(hash), Buffer.from(signature))) {
  // Request is verified
} else {
  // Request could not be verified
}

Keep your webhook secret safe. If you believe it has been compromised, delete the webhook and create a new one to receive a fresh secret. Do not commit your webhook secret to version control.


Retry behavior

When a webhook delivery fails (non-2xx response or timeout after 30 seconds), ITSM will automatically retry the delivery up to 3 times using exponential backoff.

Retry schedule

  • Name
    Attempt 1
    Type
    immediate
    Description

    The initial delivery attempt, sent immediately when the event occurs.

  • Name
    Attempt 2
    Type
    after ~1 minute
    Description

    First retry, sent approximately 1 minute after the initial failure.

  • Name
    Attempt 3
    Type
    after ~5 minutes
    Description

    Second retry, sent approximately 5 minutes after the previous failure.

After all retry attempts are exhausted, the delivery is marked as failed and no further attempts are made. You can monitor failed deliveries using the delivery history endpoint.

If a webhook consistently fails (more than 10 consecutive failed deliveries), ITSM will automatically disable the webhook and send a notification to the organization admin email. You can re-enable it from the dashboard or via the update endpoint after resolving the issue.

Failed delivery record

{
  "id": "dlv_789xyz",
  "webhook_id": "whk_abc123",
  "event_id": "evt_abc123",
  "event_type": "ticket.created",
  "status": "failed",
  "attempts": [
    {
      "attempt": 1,
      "response_code": 500,
      "response_time_ms": 1203,
      "attempted_at": "2026-02-24T10:00:00Z"
    },
    {
      "attempt": 2,
      "response_code": 500,
      "response_time_ms": 2105,
      "attempted_at": "2026-02-24T10:01:02Z"
    },
    {
      "attempt": 3,
      "response_code": 500,
      "response_time_ms": 3021,
      "attempted_at": "2026-02-24T10:06:15Z"
    }
  ],
  "failed_at": "2026-02-24T10:06:15Z",
  "created_at": "2026-02-24T10:00:00Z"
}

Best practices

To make the most of ITSM webhooks and build reliable integrations, follow these recommendations:

  • Respond quickly. Return a 200 status code as soon as you receive the webhook. Process the event asynchronously using a background job queue to avoid timeouts.
  • Handle duplicates. Use the event id to deduplicate. In rare cases (e.g., network issues during delivery acknowledgement), the same event may be delivered more than once.
  • Verify signatures. Always validate the x-itsm-signature header before processing any webhook payload. Reject requests with invalid signatures.
  • Subscribe selectively. Only subscribe to the event types your integration needs. This reduces unnecessary traffic and simplifies your handler logic.
  • Monitor delivery health. Periodically check the delivery history endpoint or set up alerts for failed deliveries to catch integration issues early.
  • Use HTTPS. Webhook URLs must use HTTPS. Plain HTTP endpoints will be rejected during registration.

Recommended handler pattern

import { createHmac, timingSafeEqual } from 'crypto'
import { enqueue } from './job-queue'

export async function handleWebhook(req, res) {
  // 1. Verify signature
  const signature = req.headers['x-itsm-signature']
  const payload = JSON.stringify(req.body)
  const hash = createHmac('sha256', process.env.WEBHOOK_SECRET)
    .update(payload)
    .digest('hex')

  if (!timingSafeEqual(Buffer.from(hash), Buffer.from(signature))) {
    return res.status(401).json({ error: 'Invalid signature' })
  }

  // 2. Acknowledge immediately
  res.status(200).json({ received: true })

  // 3. Process asynchronously
  await enqueue('webhook.process', {
    eventId: req.body.id,
    type: req.body.type,
    payload: req.body.payload,
  })
}

Was this page helpful?