Zendesk webhooks: triggers, event subscriptions and signature checks

To test a Zendesk webhook, set its endpoint URL to a free webhooks.sh bin (no signup, 48 hours, 100 requests a day) and use Test webhook or a trigger to see each request live. Zendesk signs every request with HMAC-SHA256, times out after 12 seconds and retries a few errors.

A Zendesk webhook sends an HTTP request to your endpoint when a subscribed Zendesk event or a trigger fires, signed with an x-zendesk-webhook-signature header. Pointing the endpoint at a free bin and clicking Test webhook shows the exact request headers and body right away. This page covers setup, events versus triggers, signature checks, authentication and the retry rules.

Send a Zendesk webhook to a free bin

  1. Create a free bin in your browser at view.webhooks.page or run curl -X POST https://webhooks.sh/v1/bins to get your ingest_url and view_url.
  2. In Zendesk Admin Center, go to Apps and integrations > Webhooks > Webhooks > Create webhook, and pick either Zendesk events or Trigger or Automation.
  3. Give the webhook a name, paste the bin's ingest URL as the endpoint (such as https://<bin>.webhooks.page/zendesk), and select POST, JSON and no authentication.
  4. Click Test webhook, choose an event, and click Send test; the request shows up live in your bin while Zendesk's panel displays the bin's 200 response.
  5. Create the webhook, or if you picked a trigger webhook, connect it to a trigger first and click Finish setup.

You can also create the same webhook through the Zendesk API with POST /api/v2/webhooks.

# 1. Create a free bin (or open view.webhooks.page)
curl -X POST https://webhooks.sh/v1/bins

# 2. Or create a trigger webhook with the API
curl https://<subdomain>.zendesk.com/api/v2/webhooks \
  -u <email>/token:<api_token> \
  -H "Content-Type: application/json" \
  -d '{"webhook": {
        "name": "Bin test",
        "endpoint": "https://<bin>.webhooks.page/zendesk",
        "http_method": "POST",
        "request_format": "json",
        "status": "active",
        "subscriptions": ["conditional_ticket_events"]}}'

Zendesk events or a trigger?

Zendesk locks in the connection method when you create the webhook, so you have to pick between Zendesk events and Trigger or Automation up front.

To wire up a trigger webhook, go to Admin Center > Objects and rules > Business rules > Triggers, add the Notify webhook action to a trigger, select your webhook, and write the payload using Zendesk placeholders. If you're using an automation instead, add the Notifications: Notify webhook action.

SettingZendesk eventsTrigger or automation
Fires onSubscribed events: users, organizations, articles, messaging, tickets and moreConditions you set in the trigger or automation
HTTP methodPOST onlyYour choice
BodyFixed JSON event schemaJSON, XML or form-encoded, up to 16,000 characters
Change method laterNoNo

Verify x-zendesk-webhook-signature

Every delivery includes five headers: x-zendesk-account-id, x-zendesk-webhook-id, x-zendesk-webhook-invocation-id, x-zendesk-webhook-signature and x-zendesk-webhook-signature-timestamp. As detailed in Zendesk's webhook verification docs, the x-zendesk-webhook-signature value is the base64-encoded HMAC-SHA256 digest of the x-zendesk-webhook-signature-timestamp value concatenated directly with the raw request body, hashed using the webhook's signing secret. You can get that secret in Admin Center by clicking Reveal secret or through the API.

Zendesk only generates a real signing secret once you finish creating the webhook. If you use Test webhook during creation, Zendesk signs the test payload with a static secret (dGhpc19zZWNyZXRfaXNfZm9yX3Rlc3Rpbmdfb25seQ==) instead.

All five x-zendesk headers stay visible in a webhooks.sh bin, so you can copy a captured timestamp, raw body and signature straight into the HMAC signature verifier (set it to Custom HMAC, SHA-256 and base64, and put the timestamp right in front of {body} in the message template).

import { createHmac, timingSafeEqual } from "node:crypto";

export function isFromZendesk(headers, rawBody, signingSecret) {
  const ts = headers["x-zendesk-webhook-signature-timestamp"] ?? "";
  const sig = headers["x-zendesk-webhook-signature"] ?? "";
  const expected = createHmac("sha256", signingSecret)
    .update(ts + rawBody)
    .digest("base64");
  const a = Buffer.from(expected);
  const b = Buffer.from(sig);
  return a.length === b.length && timingSafeEqual(a, b);
}

Authentication and what the bin shows

Zendesk webhooks can be sent with no authentication, an API key in a header name you choose, basic authentication or a bearer token. If you test an authenticated webhook against a bin, keep in mind that the viewer redacts Authorization and any header whose name contains cookie, key, token or secret. You'll see that the header arrived, not the credential itself, while x-zendesk-webhook-signature and the other Zendesk headers remain readable.

If you add custom headers in Zendesk, these limits apply:

  • You can add up to 5 custom headers per webhook.
  • The webhook endpoint must use HTTPS.
  • Header names can be up to 128 characters.
  • Header values can be up to 1,000 characters.
  • Names starting with x-zendesk and several standard headers aren't allowed.
  • Don't put credentials in custom headers.

Retries, timeouts and the circuit breaker

Zendesk retries failed deliveries on a few specific status codes and uses a circuit breaker to pause endpoints that return too many errors.

Zendesk makes a best effort to deliver each event once, but the same action can still fire a webhook more than once, and events can be dropped while the circuit breaker is tripped. Make your handler idempotent and use x-zendesk-webhook-signature to catch duplicate deliveries. You can also query the last 7 days of delivery history via GET /api/v2/webhooks/{webhook_id}/invocations (pass filter[status]=failed to narrow it down) and /invocations/{invocation_id}/attempts.

Because a free bin answers every valid request with HTTP 200, testing against it won't trigger retries or trip the circuit breaker as long as you stay under 100 requests a day and 100KB bodies. See the webhook retries guide for more on how senders handle failures.

SituationWhat Zendesk does
Endpoint returns 409Retries up to 3 times
429 or 503 with retry-after under 60 secondsRetries
No response within 12 secondsTimes out, retries up to 5 times
70% errors in 5 minutes, or over 1,000 errorsCircuit breaker blocks requests for 5 seconds
Fewer than 100 requests in 5 minutesCircuit breaker never triggers
Many failures in a rowWebhook stays active

Keep the URL or move to localhost

A free bin lasts 48 hours and accepts 100 requests a day, which is plenty for building triggers and checking payloads. If you want to keep using the same URL past 48 hours, claim the bin with a free Google sign-in before it expires to stop the timer and keep a rolling 30 days of request history.

When you need a permanent named URL on webhooks.host or higher volume, Solo ($12 a month, see pricing) gives you 250,000 requests a month, 5MB bodies and 90 days of history. It also includes the wsh CLI, so you can run wsh listen <endpoint> --to localhost:3000 to relay live Zendesk deliveries straight to your local server.

FAQ

How do I send a Zendesk webhook when a ticket is created?

Create a webhook connected to Trigger or Automation, then go to Objects and rules > Business rules > Triggers and build a trigger for new tickets. Add the Notify webhook action to that trigger, select your webhook, and write the JSON payload.

Can I change a Zendesk webhook from events to a trigger?

No. Zendesk fixes the connection method when you create the webhook, so you'll need to create a new one if you want to switch between Zendesk events and Trigger or Automation.

How do I verify a Zendesk webhook signature?

Concatenate the x-zendesk-webhook-signature-timestamp header value and the raw request body, compute the HMAC-SHA256 hash using the webhook's signing secret, and base64-encode the result. Compare that string to the x-zendesk-webhook-signature header.

Which secret signs Zendesk test requests?

Test requests sent while you're still creating a webhook are signed with Zendesk's static test secret, dGhpc19zZWNyZXRfaXNfZm9yX3Rlc3Rpbmdfb25seQ==, because Zendesk only generates a webhook's own signing secret after creation finishes.

Does Zendesk retry failed webhooks?

Yes. Zendesk retries 409 responses up to 3 times, 429 and 503 responses if they include a retry-after header under 60 seconds, and requests that time out after 12 seconds up to 5 times. Consecutive failures won't deactivate the webhook.

Why can't I see my API key in the test bin?

The viewer automatically redacts the values of headers whose names contain authorization, cookie, key, token or secret.

Last updated

Try it in 10 seconds.

Create a free bin at view.webhooks.page — no signup, no install. Point your sender at the URL and watch requests land live. Bins expire after 48 hours.