---
title: "Zendesk Webhooks: Setup, Signatures and Payload Examples | WebhookRelay"
meta:
  "og:description": "Create Zendesk webhooks the right way: triggers vs event subscriptions, the payload format, verifying X-Zendesk-Webhook-Signature, retries, and a fast local dev loop."
  "og:title": "Zendesk Webhooks: Setup, Signatures and Payload Examples"
  description: "Create Zendesk webhooks the right way: triggers vs event subscriptions, the payload format, verifying X-Zendesk-Webhook-Signature, retries, and a fast local dev loop."
---

![Stripes](https://webhookrelay.com/blog/zendesk-webhooks-guide/images/stripes.svg)

# **Zendesk Webhooks: Setup, Signatures and Payload Examples**

Create Zendesk webhooks the right way: triggers vs event subscriptions, the payload format, verifying X-Zendesk-Webhook-Signature, retries, and a fast local dev loop.

![Zendesk webhooks guide](https://webhookrelay.com/blog/zendesk-webhooks-guide/images/blog/heroes/route.jpg)

Zendesk's webhooks moved on from the old "HTTP targets" years ago — today there is one **Webhooks** system in Admin Center that can be wired to business rules _or_ subscribe directly to typed events, with proper HMAC signatures on every delivery. This guide covers choosing between those two invocation styles, what the payloads look like, verifying the signature, and testing against real deliveries.

## [Create the webhook](#create-the-webhook)

**Admin Center → Apps and integrations → Webhooks → Create webhook.** The first decision is how it gets invoked:

- **Trigger or automation** — the webhook fires when a business rule matches (ticket created, priority changed to urgent, SLA about to breach…). The request body is whatever the rule author builds, usually JSON with placeholders. Great when a human designs the notification.
- **Zendesk events** — the webhook subscribes to typed event streams like `zen:event-type:ticket.status_changed`, `ticket.priority_changed`, or user/organization events. The body is a **fixed envelope** Zendesk defines. This is what you want for programmatic consumers.

Then set the endpoint URL (public HTTPS), request method, and optional authentication (basic auth, bearer token, or API key header) — this auth is _outbound_ config Zendesk attaches to requests, separate from the signature below.

## [The payload](#the-payload)

Event-subscription deliveries share a stable envelope:

```
{
  "account_id": 10000001,
  "id": "01JEXAMPLEEVENT0000000000",
  "subject": "zen:ticket:15492",
  "time": "2026-01-15T10:30:00Z",
  "type": "zen:event-type:ticket.status_changed",
  "zendesk_event_version": "2022-11-06",
  "detail": {
    "id": "15492",
    "status": "open",
    "priority": "normal",
    "subject": "Cannot reset my password",
    "assignee_id": "400000000001"
  },
  "event": { "previous": "new", "current": "open" }
}
```

`type` names the event, `detail` carries the entity snapshot, and `event` describes the change itself (previous → current). Trigger-based webhooks look however the rule author shaped them — which is exactly why you should capture a real one before writing parsing code: point the webhook at a free [Webhook Bin](https://webhookrelay.com/blog/zendesk-webhooks-guide/webhook-bin/) for a minute (the bin's sample catalog also has a ready-made `ticket.status_changed` event with the full header set).

## [Verify the signature](#verify-the-signature)

Every delivery — regardless of invocation style — carries:

```
X-Zendesk-Webhook-Signature: <base64 HMAC-SHA256>
X-Zendesk-Webhook-Signature-Timestamp: 2026-01-15T10:30:00Z
```

The signed string is the **timestamp concatenated directly with the raw body** (no separator):

```
const expected = crypto.createHmac('sha256', secret)
  .update(timestamp + rawBody)
  .digest('base64');
```

The signing secret lives on the webhook's detail page (**Reveal secret**) or via `GET /api/v2/webhooks/{id}/signing_secret`. Two details that cause most failures: hashing a re-serialized body instead of the raw bytes, and forgetting the timestamp prefix. Check your implementation interactively with our free [Zendesk webhook signature verifier](https://webhookrelay.com/blog/zendesk-webhooks-guide/verify-zendesk-webhook-signature/) — and reject deliveries whose timestamp is stale to block replays.

## [Delivery, retries and the activity log](#delivery-retries-and-the-activity-log)

Zendesk expects a fast 2xx. Failed deliveries are retried a limited number of times with backoff, and webhooks that keep failing get **automatically deactivated** — a surprise you discover days later when tickets stop syncing. Defenses:

1. Acknowledge immediately, process asynchronously.
2. Watch the **activity log** on the webhook's admin page — it records each attempt, response code and latency.
3. If your consumer is flaky or occasionally deployed, front it with a stable relay endpoint that stores every event and [keeps retrying towards your side](https://webhookrelay.com/blog/zendesk-webhooks-guide/features/durable-retries/) — Zendesk always sees a healthy 200 and never deactivates the webhook.

## [Local development](#local-development)

Zendesk needs a public URL, your handler runs on `localhost` — the standard bridge applies: inspect in a [Webhook Bin](https://webhookrelay.com/blog/zendesk-webhooks-guide/webhook-bin/), then `relay forward --bucket zendesk http://localhost:8080/webhook`. Full walkthrough: [Test Zendesk webhooks locally](https://webhookrelay.com/blog/zendesk-webhooks-guide/blog/receive-zendesk-webhooks-locally/).

## [Related reading](#related-reading)

- [Test Zendesk webhooks locally](https://webhookrelay.com/blog/zendesk-webhooks-guide/blog/receive-zendesk-webhooks-locally/)
- [Zendesk webhook signature verifier](https://webhookrelay.com/blog/zendesk-webhooks-guide/verify-zendesk-webhook-signature/) — free, in-browser
- [Verify a webhook signature](https://webhookrelay.com/blog/zendesk-webhooks-guide/blog/verify-webhook-signature/)
- [What is a webhook](https://webhookrelay.com/blog/zendesk-webhooks-guide/blog/what-is-webhook/)