Zendesk Webhooks Explained (How Zendesk Pushes Data Out)
Most Zendesk integrations are built around the API — your code asks Zendesk for data, and Zendesk answers. That works beautifully until you need to react the instant something happens. Polling the API every few seconds to ask "any new urgent tickets yet?" is wasteful, slow, and rate-limited. Webhooks flip the relationship: instead of you pulling data out, Zendesk pushes data to you the moment an event occurs.
If you've ever wanted a Slack ping when a VIP files a ticket, a CRM record updated the second a customer's details change, or a custom alert fired on a specific tag, webhooks are the mechanism. This guide explains what a Zendesk webhook actually is, how you create one and fire it from a trigger, what the request looks like on the receiving end (headers, JSON payload, authentication), how to verify it's genuinely from Zendesk, and how Zendesk handles retries and failures. All of it is verified against Zendesk's developer documentation.
What a webhook is — the doorbell analogy
The cleanest way to understand a webhook is to contrast it with the API.
- The API is like checking the door. You walk over, open it, and look to see if anyone's there. To stay current you have to keep checking — that's polling, and it's the model behind most Zendesk API integrations.
- A webhook is the doorbell. You don't check anything. When someone arrives, the bell rings and you go answer it. The event comes to you.
In technical terms: a webhook is a user-defined HTTP callback. You give Zendesk an endpoint URL (a web address you control) and tell it which events you care about. When one of those events happens, Zendesk sends an HTTP request — an outbound POST with a JSON payload — to your URL. Your server receives it and does whatever you've programmed: post to Slack, write to a database, kick off a workflow.
So the API and webhooks are two halves of a whole. The API is pull (request/response, you initiate). Webhooks are push (event-driven, Zendesk initiates). Real integrations usually use both — a webhook tells you something happened, and you call the API to fetch the full detail or write something back.
How Zendesk webhooks work
In Zendesk, a webhook is a reusable connection object — it stores the destination URL, the HTTP method, the request format, the authentication, and any custom headers. On its own it does nothing; it has to be invoked. There are two distinct ways to invoke one, and which you pick at creation time determines what the webhook can do.
1. Connect it to a trigger or automation (the common path). This is how you react to ticket activity. You create the webhook with a special subscription value, conditional_ticket_events, then point a trigger or automation at it. The trigger decides when (its conditions — e.g. "priority is Urgent") and the webhook decides where (the endpoint). The trigger fires the webhook through the "Notify active webhook" action, where you also supply the request body it should send.
2. Subscribe it to Zendesk events directly. Instead of going through a trigger, the webhook subscribes to specific platform events — when a user, organization, article, community post, or agent availability record changes, for example. These are configured as event subscriptions and fire automatically when the matching event occurs, no business rule required.
One important rule from the docs: these two modes are mutually exclusive. A webhook subscribed to Zendesk events can't be connected to a trigger or automation, and vice versa. If you need both kinds of reaction, you build two webhooks.
You can create and manage webhooks in Admin Center → Apps and integrations → Webhooks, or programmatically via the Webhooks API. The API view makes the structure obvious. Here's a webhook built to be fired by a trigger, authenticated with a bearer token:
{
"webhook": {
"name": "Notify Slack of urgent tickets",
"status": "active",
"endpoint": "https://hooks.example.com/zendesk/urgent",
"http_method": "POST",
"request_format": "json",
"subscriptions": ["conditional_ticket_events"],
"authentication": {
"type": "bearer_token",
"add_position": "header",
"data": { "token": "••••••••••••" }
},
"custom_headers": { "X-Source": "zendesk" }
}
}
The required fields are the name, a status (active or inactive), the endpoint URL, the http_method, the request_format, and the subscriptions array. For event subscriptions the method must be POST and the format json; webhooks fired by triggers or automations may use POST, PUT, or PATCH with JSON (or XML). You can attach up to five custom headers (header names up to 128 characters, values up to 1,000), and the endpoint must be HTTPS for them.
Firing a webhook from a trigger
This is the pattern most teams actually use, so it's worth seeing end to end. Say you want a Slack message whenever an Urgent ticket comes in.
- Create the webhook (as above) pointing at your Slack-receiving endpoint, with
subscriptions: ["conditional_ticket_events"]. - Create a trigger with a condition like Ticket → Priority → Is → Urgent.
- Add the action "Notify active webhook," select your webhook, and write the JSON body to send. Zendesk placeholders let you inject live ticket data into that body.
The body you author on the trigger looks like this — the {{...}} placeholders are filled in with the actual ticket's values at fire time:
{
"text": "🚨 Urgent ticket #{{ticket.id}}: {{ticket.title}}",
"requester": "{{ticket.requester.name}}",
"priority": "{{ticket.priority}}",
"url": "{{ticket.link}}"
}
Now, the moment any ticket hits Urgent and the trigger's conditions match, Zendesk POSTs that JSON to your endpoint. The trigger owns the logic; the webhook owns the delivery. (Because triggers run on a ticket's create/update events, this is real-time — not a scheduled poll.)
What the request looks like on your end
When the webhook fires, your server receives a standard HTTP request. Beyond your custom headers and the JSON body, Zendesk attaches a few headers of its own that are worth knowing:
x-zendesk-account-id— which Zendesk account sent it.x-zendesk-webhook-signature— the cryptographic signature (more on this next).x-zendesk-webhook-signature-timestamp— when the request was signed.
For webhooks that subscribe to events (rather than being fired by a trigger), the JSON body follows a consistent envelope. A user event, for example, looks like this:
{
"type": "zen:event-type:user.alias_changed",
"account_id": 12514403,
"id": "6b9bbadf-5725-4e92-bebe-7b71011bf5f1",
"subject": "zen:user:6596848315901",
"time": "2099-07-04T05:33:18Z",
"zendesk_event_version": "2022-06-20",
"detail": {
"email": "[email protected]",
"role": "end-user",
"organization_id": "360000000001",
"updated_at": "2099-07-04T05:33:18Z"
}
}
The envelope is predictable: a type naming the event, the account_id, a unique id, the subject it concerns, a time, a versioned zendesk_event_version, and a detail object carrying the changed resource. For trigger-fired webhooks, the body is whatever you defined on the trigger — there's no fixed schema, because you author it.
Authentication: proving the webhook may act
When your endpoint needs to know the caller is allowed in, Zendesk supports four authentication choices on the webhook itself:
| Method | How it works | Good for |
|---|---|---|
| No auth | Nothing added | Public endpoints, or where signature verification is your only check |
| API key | A name/value pair sent as a header | Simple shared-secret endpoints |
| Basic auth | A username and password | Endpoints that expect HTTP Basic |
| Bearer token | An OAuth access token in the Authorization header | OAuth-protected APIs |
Authentication is about your destination accepting the request. Verifying the request truly came from Zendesk is a separate concern — and that's what the signature is for.
Signing and verification — is this really Zendesk?
Your endpoint is a public URL. Anyone who discovers it could POST fake payloads at it. So every Zendesk webhook request carries a digital signature you can check against a signing secret.
Here's the mechanism, exactly as documented. When you create a webhook, Zendesk generates a unique signing secret for it. On every request, Zendesk computes:
signature = base64( HMAC-SHA256( timestamp + body, signing_secret ) )
It sends that signature in the x-zendesk-webhook-signature header and the timestamp in x-zendesk-webhook-signature-timestamp. To verify, your server recomputes the same HMAC over the received timestamp + raw body using your copy of the secret, then compares. If they match, the request is authentic and untampered; if not, reject it. A minimal Node check:
const crypto = require("crypto");
function isFromZendesk(signature, timestamp, rawBody, secret) {
const data = timestamp + rawBody;
const expected = crypto
.createHmac("sha256", secret)
.update(data)
.digest("base64");
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(signature)
);
}
Two practical notes. Hash the raw request body (re-serializing parsed JSON can change bytes and break the match). And Zendesk publishes a fixed test secret — dGhpc19zZWNyZXRfaXNfZm9yX3Rlc3Rpbmdfb25seQ== — so you can validate your verification code before going live. Treat your real signing secret like any other credential: never commit it to source control.
Retries and failures
Networks fail, and your endpoint will occasionally be down or slow. Zendesk builds in resilience so a single hiccup doesn't silently drop an event:
- Timeouts. Zendesk waits up to 12 seconds for your endpoint to respond, and will retry up to 5 times on timeout. Your handler should return quickly (a 2xx) and do heavy work asynchronously.
- Conflicts. On an HTTP 409 it retries up to 3 times.
- Circuit breaker. If a webhook starts failing en masse — roughly 70% of requests failing, or 1,000+ errors within a 5-minute window — Zendesk trips a circuit breaker and briefly pauses the webhook to avoid hammering a broken endpoint.
- Activity log. Each webhook keeps a 7-day log of its invocations, so you can see what fired, what the response was, and what failed when you're debugging.
The takeaway for builders: respond fast, return the right status codes, and make your handler idempotent — because a retry means the same event can legitimately arrive more than once.
Webhooks vs. the API vs. native integrations
These three approaches overlap, and the right one depends on the job:
- Webhooks are best when you need to react in real time to events and push data outward — alerts, syncs, custom workflows. Low overhead, event-driven, but you have to host and secure an endpoint.
- The API is best when you need to pull data on demand or write back into Zendesk — fetch a ticket's history, bulk-update fields, run a report. Often a webhook fires first ("something changed"), then your code calls the API to act on it.
- Native / marketplace integrations (the prebuilt Slack, Jira, Salesforce apps) are best when a maintained connector already does what you need — no code, no endpoint to run — at the cost of flexibility.
A common, robust pattern combines all three: a native app for the heavy lifting, a webhook for the one real-time event the app misses, and the API for the custom read/write in between.
Where AI fits
Keeping external systems in sync is exactly the kind of plumbing webhooks exist for — and it's also where an AI agent layer like Macha becomes useful. Macha isn't a help desk and it's not a Zendesk replacement; it runs on top of your existing Zendesk. Webhooks (and the API) are how that layer stays current: a ticket event can notify the AI, the AI can read context and look things up across connected systems, draft a reply, set fields, and write the result back — so your help desk and your other tools never drift out of sync.
Worth being honest about the model: Macha bills per AI action — any automated step it takes, like summarizing a ticket, classifying it, looking up data in another system, drafting a reply, or resolving it — at 0.5–9 credits depending on the model you choose, not per closed ticket. That's deliberate: most automation isn't a single "resolution," it's a chain of small actions, and you pay for the work done, not a predefined outcome. If you'd rather wire up your own webhooks, that path stays fully open; if the orchestration is the part you don't want to build and maintain, that's the gap an AI layer fills. We walk through it in how to automate Zendesk with AI, and you can try it free — 7-day free trial, no credit card required.
Frequently asked questions
What is a Zendesk webhook? A webhook is a connection Zendesk uses to push data to an external URL the moment an event happens — the inverse of the API, which you pull data from. You give Zendesk an endpoint, and when a matching event occurs (a ticket meets a trigger's conditions, or a user/organization record changes), Zendesk sends an HTTP request with a JSON payload to that URL so your system can react in real time.
How do you trigger a Zendesk webhook? For ticket activity, create the webhook with the conditional_ticket_events subscription, then build a trigger (or automation) whose conditions decide when it fires. Add the "Notify active webhook" action, select your webhook, and author the JSON body — using placeholders like {{ticket.id}} to inject live ticket data. For non-ticket activity, a webhook can instead subscribe directly to platform events (user, organization, article, and more).
What authentication do Zendesk webhooks support? Four options on the outbound request: no auth, API key (a name/value header), basic auth (username and password), and bearer token (an OAuth access token). These authenticate the request to your endpoint. Verifying the request actually came from Zendesk is separate — that's done with the signature.
How do I verify a Zendesk webhook is genuine? Each webhook has a unique signing secret. On every request Zendesk sends signature = base64(HMAC-SHA256(timestamp + body)) in the x-zendesk-webhook-signature header, with the timestamp in x-zendesk-webhook-signature-timestamp. Recompute the same HMAC over the received timestamp plus the raw body using your copy of the secret and compare — if they match, it's authentic and untampered.
Does Zendesk retry a failed webhook? Yes. Zendesk waits up to 12 seconds for a response and retries up to 5 times on timeout and up to 3 times on an HTTP 409. A circuit breaker pauses a webhook that's failing en masse (about 70% of requests failing, or 1,000+ errors in 5 minutes). Each webhook keeps a 7-day activity log for debugging. Because retries can deliver the same event twice, make your handler idempotent.
What's the difference between webhooks and the Zendesk API? The API is pull: your code requests data or writes changes when you decide. Webhooks are push: Zendesk sends data to you automatically when an event fires. Most integrations use both — a webhook says "something happened," then your code calls the API to fetch detail or write back.
The bottom line
Webhooks are Zendesk's outbound voice: where the API waits to be asked, a webhook speaks up the instant something happens. You define an endpoint, attach authentication, and either fire it from a trigger for ticket events or subscribe it directly to platform events — then verify each request with the HMAC-SHA256 signature so you know it's really Zendesk. Pair them with the API for reads and writes, lean on the built-in retries and activity log for reliability, and you have the foundation for real-time alerts, CRM syncs, and any custom workflow your help desk needs to drive. For the bigger picture of how triggers and webhooks fit alongside the rest of the platform, see Zendesk business rules explained.
Webhook mechanics verified against Zendesk's official developer documentation, June 2026. Zendesk updates its platform periodically — confirm endpoints, limits, and headers in your own account and the current docs before relying on them.

