Gareth Wilson Gareth Wilson

Guide to Zendesk Webhooks Features and Best Practices

Published · Updated


Zendesk is the leading customer service platform, used by over 100,000 organizations to manage support tickets, help centers, and customer communications across channels. Beyond its core ticketing functionality, Zendesk's webhook system enables teams to build real-time integrations with external systems, and webhooks are the most flexible way to connect Zendesk activity with your broader infrastructure.

This guide covers everything you need to know about Zendesk webhooks: their features, how to configure them, best practices for production deployments, and the common pain points developers face along with solutions to address them.

What are Zendesk webhooks?

Zendesk webhooks are HTTP callbacks that send requests to a specified URL in response to activity in Zendesk Support, Guide, Gather, and Messaging. When a ticket is created, a user is updated, an article is published, or any number of other events occur, Zendesk sends an HTTP request containing event data to URLs you configure. This enables integration with CRMs, notification systems, data warehouses, custom automation, and any service that can receive HTTP requests.

Webhooks in Zendesk exist in two primary connection methods:

  • Event-subscribed webhooks subscribe directly to Zendesk system events (user created, organization updated, article published, etc.). These fire for every occurrence with a fixed JSON payload — you can't add conditions, change the HTTP method, or modify the payload. They're ideal for system-wide data synchronization.
  • Trigger/automation-connected webhooks are invoked by Zendesk business rules based on ticket activity. These support full customization: you choose the HTTP method (GET, POST, PUT, PATCH, DELETE), request format (JSON, XML, form-encoded), and define the payload using Zendesk placeholders and Liquid markup. They're ideal for conditional, ticket-specific workflows.

A webhook can only use one connection method, and you can't change it after creation.

Zendesk webhook features

FeatureDetails
Webhook configurationZendesk Admin Center UI or Webhooks API
Signing algorithmHMAC-SHA256
Timeout10-second non-configurable timeout
Retry logicUp to 3 retries for 409; retries for 429/503 with retry-after header; up to 5 retries on timeout
Circuit breakerTriggers at 70% error rate or 1,000+ errors in 5 minutes
Connection methodsZendesk event subscriptions or trigger/automation connections
Supported HTTP methodsPOST (events); GET, POST, PUT, PATCH, DELETE (triggers/automations)
Request formatsJSON, XML, form-encoded (triggers/automations); JSON only (events)
AuthenticationAPI key, Basic auth, Bearer token, or none
Custom headersUp to 5 custom headers per webhook
Manual retryNot available natively
Browsable logActivity log via API, last 7 days only
Payload size limit256 KB

Supported event types

Zendesk webhooks can subscribe to events across multiple domains:

DomainExample events
TicketTicket created, updated, solved, deleted, commented
UserUser created, updated, deleted, merged, password changed, active changed
OrganizationOrganization created, updated, deleted
ArticleArticle published, unpublished, updated, voted
Community postPost created, updated, deleted, voted
Agent availabilityAgent status changed, work item added/removed
Omnichannel routingConfiguration changed
MessagingConversation started, message sent
Live messaging metricsMetrics updated

Event-subscribed webhooks fire for every occurrence of the subscribed event — there are no conditions or filters. Filtering must be handled by the receiving endpoint.

Webhook payload structure

Event-subscribed webhooks

When a Zendesk event fires, the webhook delivers a JSON payload with a standardized schema:

{
  "type": "zen:event-type:user.created",
  "account_id": 12514403,
  "id": "2b24ef10-19d4-4740-93cf-8f98ec4776c0",
  "time": "2025-07-04T05:33:18Z",
  "zendesk_event_version": "2022-06-20",
  "subject": "zen:user:6596848315901",
  "detail": {
    "created_at": "2025-07-04T05:27:58Z",
    "email": "user.name@example.com",
    "external_id": "",
    "default_group_id": "0",
    "id": "6596848315901",
    "organization_id": "0",
    "role": "end-user",
    "updated_at": "2025-07-04T05:33:18Z"
  },
  "event": {
    "current": true
  }
}

Key payload fields

FieldDescription
typeThe event type, e.g. zen:event-type:user.created
account_idZendesk account ID associated with the event
idUnique identifier for the event
timeTimestamp for when the event occurred
subjectContains the event's domain and resource ID
detailResource data created or changed by the event
eventContains information about the event's changes (current and previous values)
zendesk_event_versionSchema version string

Trigger/automation-connected webhooks

For webhooks connected to triggers or automations, you define the payload yourself using Zendesk placeholders:

{
  "ticket_id": "{{ticket.id}}",
  "status": "{{ticket.status}}",
  "requester": "{{ticket.requester.name}}",
  "assignee": "{{ticket.assignee.name}}",
  "subject": "{{ticket.title}}",
  "priority": "{{ticket.priority}}",
  "link": "{{ticket.link}}"
}

Zendesk renders placeholders at send time, replacing them with actual ticket data. Liquid markup is also supported for more advanced template logic.

Request headers

All Zendesk webhook requests include the following standard headers:

HeaderDescription
x-zendesk-account-idYour Zendesk account ID
x-zendesk-webhook-idUnique ID of the webhook
x-zendesk-webhook-invocation-idUnique identifier for this specific invocation
x-zendesk-webhook-signatureBase64-encoded HMAC-SHA256 signature
x-zendesk-webhook-signature-timestampTimestamp used in signature computation

Security with HMAC signatures

Zendesk supports HMAC-SHA256 signatures to verify webhook authenticity. Each webhook can have a signing secret, and Zendesk signs every request by computing an HMAC over the concatenation of the timestamp and the request body. The signature is included in the x-zendesk-webhook-signature header.

You can retrieve the signing secret from the webhook details page in Admin Center or via the Show Webhook Signing Secret API. The secret should be treated like any other credential and never committed to source code.

When testing a webhook before it's fully created, Zendesk uses a static signing secret: dGhpc19zZWNyZXRfaXNfZm9yX3Rlc3Rpbmdfb25seQ==. Once created, each webhook gets its own unique signing secret.

Authentication options

Zendesk webhooks support multiple authentication methods for requests sent to your endpoint:

MethodConfiguration
API keyHeader name and key value
Basic authUsername and password
Bearer tokenOAuth 2.0-style bearer token
NoneNo authentication (not recommended)

All authentication types should only be used over HTTPS. You can also add up to five custom headers for additional integration needs, though authentication credentials should not be placed in custom headers.

Custom payload templates

For trigger/automation-connected webhooks, Zendesk supports payload customization using template placeholders. You define the payload in the trigger or automation action, using {{placeholder}} syntax that Zendesk renders at send time:

  • {{ticket.id}}, {{ticket.status}}, {{ticket.priority}} — ticket fields
  • {{ticket.requester.name}}, {{ticket.requester.email}} — requester details
  • {{ticket.assignee.name}}, {{ticket.group.name}} — assignment data
  • {{ticket.link}}, {{ticket.title}} — ticket metadata
  • {{ticket.latest_public_comment}} — latest comment content
  • {{current_user.name}}, {{current_user.email}} — acting user

Liquid markup is also supported for conditional logic, iteration (such as looping through ticket CCs), and string manipulation within payloads. If a placeholder references a blank field, it renders as an empty string.

The payload size limit is 256 KB. For webhooks using GET or DELETE methods, you can add custom URL parameters instead of a request body.

Setting up Zendesk webhooks

Via the Admin Center UI

  1. Navigate to Apps and integrations > Webhooks in Admin Center
  2. Click Create webhook
  3. Choose your connection method:
    • Zendesk events — select one or more event types to subscribe to
    • Trigger or automation — select this to connect the webhook to ticket-based business rules
  4. Enter a name and the endpoint URL
  5. Configure the request method and format (trigger/automation webhooks only)
  6. Set up authentication (API key, Basic auth, or Bearer token)
  7. Optionally add custom headers (up to 5)
  8. Click Test to send a sample payload and verify connectivity
  9. Click Create webhook

For trigger/automation webhooks, you then need to connect the webhook to a trigger or automation under Objects and rules > Business rules.

Via the Webhooks API

Create an event-subscribed webhook:

curl -X POST https://{subdomain}.zendesk.com/api/v2/webhooks \
  -u {email_address}/token:{api_token} \
  -H "Content-Type: application/json" \
  -d '{
    "webhook": {
      "name": "User Events Webhook",
      "status": "active",
      "endpoint": "https://your-endpoint.com/webhooks/zendesk",
      "http_method": "POST",
      "request_format": "json",
      "subscriptions": [
        "zen:event-type:user.created",
        "zen:event-type:organization.created"
      ],
      "authentication": {
        "type": "bearer_token",
        "data": {
          "token": "YOUR_TOKEN"
        },
        "add_position": "header"
      }
    }
  }'

Create a trigger-connected webhook:

curl -X POST https://{subdomain}.zendesk.com/api/v2/webhooks \
  -u {email_address}/token:{api_token} \
  -H "Content-Type: application/json" \
  -d '{
    "webhook": {
      "name": "Ticket Notification Webhook",
      "status": "active",
      "endpoint": "https://your-endpoint.com/webhooks/zendesk-tickets",
      "http_method": "POST",
      "request_format": "json",
      "subscriptions": ["conditional_ticket_events"],
      "authentication": {
        "type": "api_key",
        "data": {
          "name": "X-API-Key",
          "value": "YOUR_API_KEY"
        },
        "add_position": "header"
      }
    }
  }'

Connecting a webhook to a trigger

After creating a trigger-connected webhook, link it to a trigger using the notification_webhook action:

{
  "actions": [
    {
      "field": "notification_webhook",
      "value": [
        "WEBHOOK_ID",
        "{\"ticket_id\": \"{{ticket.id}}\", \"status\": \"{{ticket.status}}\", \"requester\": \"{{ticket.requester.name}}\"}"
      ]
    }
  ]
}

Best practices when working with Zendesk webhooks

Verifying HMAC signatures

When processing webhooks from Zendesk, verify the HMAC signature to ensure requests genuinely originated from your Zendesk instance.

Node.js

const crypto = require('crypto');
const express = require('express');
const app = express();

// Capture raw body for signature verification
app.use(express.json({
  verify: (req, res, buf) => {
    req.rawBody = buf.toString('utf8');
  }
}));

function verifyZendeskSignature(rawBody, signature, timestamp, secret) {
  const message = timestamp + rawBody;
  const hash = crypto
    .createHmac('sha256', secret)
    .update(message, 'utf8')
    .digest('base64');

  return crypto.timingSafeEqual(
    Buffer.from(hash),
    Buffer.from(signature)
  );
}

app.post('/webhooks/zendesk', (req, res) => {
  const signature = req.headers['x-zendesk-webhook-signature'];
  const timestamp = req.headers['x-zendesk-webhook-signature-timestamp'];

  if (!verifyZendeskSignature(
    req.rawBody,
    signature,
    timestamp,
    process.env.ZENDESK_WEBHOOK_SECRET
  )) {
    return res.status(401).send('Invalid signature');
  }

  // Verify timestamp is recent (prevent replay attacks)
  const requestTime = new Date(timestamp).getTime();
  const now = Date.now();
  if (Math.abs(now - requestTime) > 300000) { // 5 minutes
    return res.status(401).send('Request too old');
  }

  // Process webhook
  res.status(200).send('OK');
});

Python

import hmac
import hashlib
import base64
import os
from datetime import datetime, timezone, timedelta
from flask import Flask, request, abort

app = Flask(__name__)

def verify_zendesk_signature(payload, signature, timestamp, secret):
    message = timestamp + payload
    computed = base64.b64encode(
        hmac.new(
            secret.encode('utf-8'),
            message.encode('utf-8'),
            hashlib.sha256
        ).digest()
    ).decode('utf-8')
    return hmac.compare_digest(computed, signature)

@app.route('/webhooks/zendesk', methods=['POST'])
def handle_zendesk_webhook():
    signature = request.headers.get('X-Zendesk-Webhook-Signature')
    timestamp = request.headers.get('X-Zendesk-Webhook-Signature-Timestamp')

    if not verify_zendesk_signature(
        request.get_data(as_text=True),
        signature,
        timestamp,
        os.environ['ZENDESK_WEBHOOK_SECRET']
    ):
        abort(401)

    # Verify timestamp is recent
    request_time = datetime.fromisoformat(timestamp.replace('Z', '+00:00'))
    if abs((datetime.now(timezone.utc) - request_time).total_seconds()) > 300:
        abort(401)

    # Process webhook
    return 'OK', 200

Important: Use a constant-time comparison function (like crypto.timingSafeEqual in Node.js or hmac.compare_digest in Python) to prevent timing attacks. Zendesk's own sample code uses buffer.compare(), which is not constant-time.

Respond immediately and process asynchronously

Zendesk has a 10-second timeout for webhook deliveries. If your endpoint takes longer to respond, Zendesk considers the delivery failed and will retry, potentially causing duplicate processing. Always acknowledge webhooks immediately and handle processing in a background job.

Implement idempotent processing

Zendesk explicitly states that webhooks may be delivered multiple times. Use the x-zendesk-webhook-invocation-id header to deduplicate:

async function processZendeskWebhook(payload, invocationId) {
  // Check if already processed
  const exists = await redis.get(`processed:${invocationId}`);
  if (exists) {
    console.log(`Invocation ${invocationId} already processed, skipping`);
    return;
  }

  // Process the webhook
  await handleWebhook(payload);

  // Mark as processed with TTL
  await redis.setex(`processed:${invocationId}`, 86400, '1'); // 24 hour TTL
}

Monitor webhook health proactively

Since Zendesk doesn't provide failure notifications, poll the webhook invocations API regularly to detect issues.

Zendesk webhook limitations and pain points

Non-configurable 10-second timeout

The Problem: Zendesk webhooks enforce a hardcoded 10-second timeout that cannot be adjusted through the Admin Center or API. Endpoints that require any non-trivial processing (database lookups, API calls to third-party services, or complex business logic) risk timing out and triggering retries.

Why It Happens: Zendesk's webhook system processes requests synchronously with trigger evaluation. Zendesk determined that longer timeouts would negatively impact overall system performance and ticket-saving operations.

Workarounds:

  • Always acknowledge webhooks immediately with a 200 response and process asynchronously using a job queue
  • Avoid making third-party API calls within your webhook handler before responding
  • Use a lightweight intermediary service that accepts the webhook and forwards it to your actual processor

How Hookdeck Can Help: Hookdeck accepts webhooks instantly on Zendesk's behalf and delivers them to your endpoint with configurable timeouts, giving your service additional time to process complex webhook payloads without risking failed deliveries or retries.

Limited and selective retry logic

The Problem: Zendesk's retry behavior is narrow and non-configurable. Only HTTP 409 responses are retried (up to 3 times), and 429/503 responses are only retried if they include a retry-after header with a value under 60 seconds. Common failure codes like 500, 502, and 504 are not retried at all. Timed-out requests are retried up to 5 times, but this often causes duplicate processing rather than solving delivery issues.

Why It Happens: Zendesk's retry logic is designed to be conservative to avoid overwhelming failing endpoints. However, this means transient server errors (the most common webhook failure scenario) result in permanently lost events.

Workarounds:

  • Ensure your endpoint returns appropriate status codes (always return 200 even if processing fails, and handle errors internally)
  • Implement your own retry logic by polling the invocations API for failed deliveries
  • Build a queueing layer between Zendesk and your final destination

How Hookdeck Can Help: Hookdeck provides configurable retry policies with customizable backoff strategies (linear, exponential), retry counts (up to 50 attempts over a week), and automatic retries for any failure status code — ensuring reliable delivery regardless of Zendesk's built-in limitations.

Circuit breaker silently drops events

The Problem: Zendesk's circuit breaker activates when 70% of a webhook's requests fail within a 5-minute window or when 1,000+ errors occur in 5 minutes. Once triggered, the webhook stops sending requests for 5-second intervals, and events that would have fired during these pauses are logged as "circuit broken" but never delivered. There is no mechanism to replay these dropped events.

Why It Happens: The circuit breaker protects Zendesk's infrastructure and your endpoint from being overwhelmed during outages. However, the 5-second recovery window with a single test request creates a cycle where even brief endpoint issues can cause extended periods of dropped events.

Workarounds:

  • Ensure your endpoint maintains high availability with redundancy
  • Monitor Zendesk webhook invocation logs for "circuit broken" status entries
  • Build a secondary event capture mechanism (polling Zendesk's API for changes you may have missed)

How Hookdeck Can Help: Hookdeck's ingestion infrastructure is highly available and accepts events regardless of your endpoint's status. Failed deliveries are preserved with full payload data and can be replayed manually or in bulk once issues are resolved — no events are ever silently dropped.

No dead letter queue or event replay

The Problem: When webhooks fail after all retries are exhausted, the events are permanently lost. Zendesk doesn't maintain a queue of failed deliveries that can be inspected or replayed. The invocation activity log provides visibility into failures for 7 days, but there's no mechanism to re-trigger those failed deliveries.

Why It Happens: Zendesk's webhook system is designed as a fire-and-forget notification mechanism rather than a guaranteed delivery pipeline.

Workarounds:

  • Log all received webhooks in your own system for gap analysis
  • Use Zendesk's Incremental Exports API or Search API to reconcile missed events
  • Build a polling-based backup system that periodically syncs Zendesk data

How Hookdeck Can Help: Hookdeck automatically preserves all events, including failed deliveries, in a persistent store with full observability. You can search by any field, inspect failure details, and replay individual events or failed batches in bulk — turning hours of recovery work into minutes.

Duplicate webhook deliveries

The Problem: Zendesk explicitly states that webhooks may be delivered more than once for the same event. Timeout retries, system instability, and internal processing can all cause duplicate invocations. For workflows involving payments, notifications, or data creation, duplicates can cause real business impact.

Why It Happens: Zendesk's infrastructure uses at-least-once delivery semantics (with the caveat that delivery isn't guaranteed at all in some cases). The 10-second timeout compounds this issue — endpoints that process slightly too slowly receive the webhook successfully but also get retry attempts.

Workarounds:

  • Implement idempotent webhook handlers using the x-zendesk-webhook-invocation-id header
  • Use webhook signatures to detect and correlate duplicate invocations
  • Store processed invocation IDs in a cache or database with a TTL

How Hookdeck Can Help: Hookdeck's deduplication feature can automatically filter duplicate webhooks based on payload content, headers, or custom identifiers before they reach your endpoint, ensuring each unique event is processed exactly once.

No failure notifications or alerting

The Problem: When webhooks start failing, Zendesk does not proactively notify you. There are no email alerts, Slack notifications, or any other mechanism to flag delivery issues. The only way to discover failures is to manually check the activity log for each webhook or poll the invocations API.

Why It Happens: Zendesk's webhook monitoring is limited to the activity log and API. This was a known limitation carried over from the deprecated HTTP Targets system.

Workarounds:

  • Build a scheduled job that polls the webhook invocations API for failed statuses
  • Create a Zendesk monitor alert that tracks your webhook endpoint's health externally
  • Use third-party monitoring tools to track webhook delivery success rates

How Hookdeck Can Help: Hookdeck's dashboard provides real-time visibility into webhook delivery status, latency, errors, and response codes. You can configure alerts to notify your team via email, Slack, or other channels when delivery issues occur.

Event-subscribed webhooks can't filter or customize payloads

The Problem: Webhooks subscribed to Zendesk events always use POST with a fixed JSON payload — you can't change the HTTP method, add conditions, or modify the payload structure. If you subscribe to user.created, the webhook fires for every single user creation with the same predefined payload, regardless of whether the event is relevant to your integration.

Why It Happens: Event-subscribed webhooks are designed as a simple pub/sub mechanism. Zendesk's event system doesn't support server-side filtering on event subscriptions.

Workarounds:

  • Filter irrelevant events in your webhook handler and return 200 for events you don't need
  • Use trigger-connected webhooks instead if you need conditional logic (though this limits you to ticket events)
  • Build a transformation layer between Zendesk and your final destination

How Hookdeck Can Help: Hookdeck's filtering and transformation capabilities let you define rules that inspect incoming event payloads and either route, transform, or discard them before they reach your endpoint — effectively adding the server-side filtering that Zendesk's event webhooks lack.

Seven-day activity log retention

The Problem: Zendesk's webhook activity log retains invocation data for only 7 days. After that, all delivery history, failure records, and debugging information are permanently deleted. This makes long-term auditing, compliance reporting, and historical debugging impossible.

Why It Happens: Zendesk limits log retention to manage storage costs and system performance across its multi-tenant platform.

Workarounds:

  • Export webhook invocation data regularly using the Invocations API
  • Build your own logging pipeline to persist all webhook delivery data
  • Use a third-party observability platform to capture and retain delivery logs

How Hookdeck Can Help: Hookdeck provides persistent event logs with extended retention, giving you a complete audit trail of all webhook deliveries, failures, and response data.

Can't access webhook response data in workflows

The Problem: When a webhook is invoked via a trigger or automation, Zendesk does not expose the response data from the destination endpoint. You can't use the webhook's response to update a ticket, set a field value, or drive conditional logic in subsequent workflow steps.

Why It Happens: Zendesk's webhook execution is asynchronous, so webhook jobs are queued and run independently from the trigger/automation evaluation. By the time the webhook response is received, the trigger has already completed.

Workarounds:

  • Have your webhook endpoint call Zendesk's API to update the ticket directly
  • Use Zendesk's Integration Services (ZIS) for more complex two-way workflows
  • Build a middleware layer that processes webhook responses and calls back into Zendesk

How Hookdeck Can Help: Hookdeck's transformation and routing capabilities can process webhook responses and trigger follow-up actions, enabling bidirectional workflows that Zendesk's native webhook system doesn't support.

Testing Zendesk webhooks

Use the built-in test feature

The webhook configuration page in Admin Center includes a Test button that sends a sample payload. Be aware that:

  • Test payloads use a static signing secret (dGhpc19zZWNyZXRfaXNfZm9yX3Rlc3Rpbmdfb25seQ==), not the webhook's actual secret
  • Test requests may behave differently from real event deliveries
  • Some authentication issues only surface with actual event data

Use a request inspector

Before building your handler, inspect real Zendesk payloads using a tool like Hookdeck Console:

  1. Create a Hookdeck Source URL
  2. Configure it as your webhook endpoint in Zendesk
  3. Trigger a real event (create a test user, update a ticket, etc.)
  4. Inspect the full payload structure, headers, and signature in the Hookdeck dashboard

Validate in staging

Test your webhook integration with realistic scenarios:

  • Ticket creation and update flows
  • User and organization CRUD operations
  • High-volume event bursts (bulk imports or updates)
  • Endpoint failures and circuit breaker recovery
  • Duplicate invocation handling

Conclusion

Zendesk webhooks provide a flexible foundation for integrating your customer service platform with CRMs, notification systems, data pipelines, and custom automation. The dual connection model of event subscriptions for system-wide activity and trigger/automation connections for conditional ticket workflows, covers a wide range of integration scenarios. The standardized event schema, HMAC signature verification, and multiple authentication options provide a solid starting point for secure integrations.

However, significant limitations around the non-configurable timeout, narrow retry logic, circuit breaker behavior, and lack of failure notifications mean production deployments require careful defensive programming. Implementing signature verification with constant-time comparison, idempotent processing with invocation ID tracking, and asynchronous webhook handling will address most common issues.

For straightforward integrations with reliable endpoints and moderate event volumes, Zendesk's built-in webhook system combined with proper error handling and idempotency works well. For high-volume Zendesk instances, mission-critical integrations where delivery guarantees matter, or complex multi-destination routing scenarios, webhook infrastructure like Hookdeck can address Zendesk's limitations, providing you with configurable retries, payload transformation, automatic deduplication, dead letter queue functionality, and comprehensive delivery monitoring without modifying your Zendesk configuration.


Gareth Wilson

Gareth Wilson

Product Marketing

Multi-time founding marketer, Gareth is PMM at Hookdeck and author of the newsletter, Community Inc.