Look up open Zendesk tickets from a WhatsApp chatbot. When a customer messages you asking for an update, query their tickets by email in real time and reply with the ticket status, without anyone touching Zendesk manually.
Before following this guide, read the External API Request step foundation guide. It covers every field in the step interface so this guide can focus on Zendesk-specific values.
Zendesk is removing API token authentication in phases. Unused tokens deactivate automatically from July 28, 2026. All tokens stop working permanently on April 30, 2027. This guide teaches OAuth 2.0 as the primary method. If you already have a token-based integration, it will continue to work as long as the token is used at least once every 30 days, but migrate to OAuth before the April 2027 deadline.
Zendesk has two authentication options for the REST API today. One is being deprecated; the other is the future.
| Method | Status | Format | Recommended for |
|---|---|---|---|
| OAuth 2.0 | Current and permanent | Bearer access token in Authorization header | All new integrations: use this |
| API Token (Basic Auth) | Deprecated: stops April 2027 | Base64(email/token:key) in Authorization header | Existing integrations only: migrate before April 2027 |
This guide covers OAuth 2.0 as the primary path. The API token method is shown in the troubleshooting section for reference.
OAuth clients are created in Zendesk Admin Center. This is a one-time setup that gives you a Client ID and Client Secret for generating access tokens.
https://wa.expert (or any HTTPS URL you control).Your subdomain is the unique part of your Zendesk URL. If your URL is acme.zendesk.com, your subdomain is 'acme'. All API requests go to https://YOUR_SUBDOMAIN.zendesk.com/api/v2/.
Zendesk OAuth guide: developer.zendesk.com
For a server-to-server WhatsApp bot (no user login involved), use the client_credentials grant. Add a first External API Request step to get the token, then store it as a variable for Step 3.
{
"access_token": "gErypPlm4dOVgGRvA1ZzMH5MQ3nLo8bo",
"token_type": "Bearer",
"scope": "read"
}
Map: access_token -> {{zendesk_token}}
Tokens are long-lived (no expiry by default on client_credentials).
Store in a persistent variable or re-request at the start of each conversation.With the token stored as {{zendesk_token}} and the customer's email as {{customer_email}}, add a second External API Request step:
| Field | Value | Notes |
|---|---|---|
| Select Method | GET | Searching existing tickets. |
| Request URL | https://YOUR_SUBDOMAIN.zendesk.com/api/v2/search.json?query=type:ticket+requester:{{{customer_email}}}+status:open | The Zendesk unified search endpoint. type:ticket filters to tickets. requester: filters by the ticket submitter's email. status:open shows only open tickets. Remove status:open to see all tickets. |
| Authorization | Bearer {{{zendesk_token}}} | The OAuth access token from Step 2. Include 'Bearer ' prefix. |
| Content-Type | application/json | Standard header for Zendesk API requests. |
| Select Body Type | None | GET requests carry no body. |
| Choose Response Type | JSON | Zendesk returns structured JSON. |
The ticket search requires the customer's email. Before this step, your chatbot flow must ask the customer to type their email, or collect it from a previous interaction stored in the Mini-CRM. Store it as {{customer_email}} to use in the search query.
| What you want | Query string |
|---|---|
| Open tickets by email | type:ticket requester:priya@example.com status:open |
| All tickets by email (any status) | type:ticket requester:priya@example.com |
| Tickets with a specific phrase | type:ticket priya@example.com order+not+delivered |
| Tickets by subject keyword | type:ticket subject:refund requester:priya@example.com |
| Recent tickets (last 7 days) | type:ticket requester:priya@example.com created>7days |
| Search by phone number (full text) | type:ticket 9820000001 |
Zendesk search queries use space-separated conditions. Use + for URL encoding of spaces in GET parameters. All conditions are AND by default.
A successful Zendesk ticket search response looks like this:
{
"results": [
{
"id": 1001,
"url": "https://acme.zendesk.com/api/v2/tickets/1001.json",
"subject": "Order #5521 not delivered",
"description": "My order was placed on June 18 but has not arrived.",
"status": "open",
"priority": "normal",
"requester_id": 447,
"assignee_id": 12,
"created_at": "2026-06-20T10:30:00Z",
"updated_at": "2026-06-22T14:00:00Z",
"result_type": "ticket"
}
],
"count": 1,
"next_page": null
}
Map: results[0].id, results[0].subject, results[0].status, results[0].priorityZendesk search results come under a 'results' key, not 'tickets' or 'data'. Map as results[0].subject, results[0].status. If count is 0 or results is empty, the customer has no matching open tickets. Add a conditions branch for that case.
Map these response paths to variables in the External API Request step:
| Variable name | Response path | Example value |
|---|---|---|
| ticket_id | results[0].id | 1001 |
| ticket_subject | results[0].subject | Order #5521 not delivered |
| ticket_status | results[0].status | open |
| ticket_priority | results[0].priority | normal |
| ticket_updated | results[0].updated_at | 2026-06-22T14:00:00Z |
| ticket_count | count | 1 |
ticket_count tells you how many open tickets this customer has. If count is 0, reply that no open tickets were found. If count is more than 1, mention the total and show the most recent.
If the customer messaged you first within the last 24 hours, your reply is a free service conversation. For proactive outbound ticket updates triggered from Zendesk, use an approved Utility template.
The same OAuth token lets you create tickets. When a customer describes a problem in WhatsApp, your bot can open a Zendesk ticket automatically without anyone touching Zendesk.
POST https://YOUR_SUBDOMAIN.zendesk.com/api/v2/tickets.json
Authorization: Bearer {{zendesk_token}}
Content-Type: application/json
Body:
{
"ticket": {
"subject": "WhatsApp: {{customer_message_summary}}",
"comment": {
"body": "Customer WhatsApp message: {{customer_message}}"
},
"requester": {
"name": "{{customer_name}}",
"email": "{{customer_email}}"
},
"priority": "normal",
"tags": ["whatsapp", "bot-created"]
}
}
Response:
{ "ticket": { "id": 1002, "status": "new", ... } }
Map: ticket.id -> new_ticket_id
Bot replies: "Ticket #{{new_ticket_id}} created. Our team will respond within 24 hours."Customer messages: "What is the status of my support request?"
Bot asks: "Please share the email address used to raise the ticket."
Customer replies: priya@example.com
Stored as {{customer_email}}.
Step 1 — Get token (if not already stored):
POST https://acme.zendesk.com/oauth/tokens
Body: {{"grant_type":"client_credentials","client_id":"...","client_secret":"...","scope":"read"}}
Store: zendesk_token
Step 2 — Search tickets:
GET https://acme.zendesk.com/api/v2/search.json
?query=type:ticket+requester:priya@example.com+status:open
Authorization: Bearer {{zendesk_token}}
Response: count=1
ticket_id = 1001
ticket_subject = "Order #5521 not delivered"
ticket_status = "open"
ticket_updated = "2026-06-22T14:00:00Z"
Conditions: if count = 0 → "No open tickets found for this email."
Bot replies (if found):
"Hi Priya, here is your ticket update:
Ticket #1001: Order #5521 not delivered
Status: Open
Last updated: 22 June 2026
Our team is working on it. Reply AGENT to request a callback,
or CLOSE to mark this as resolved."If you have an existing Zendesk integration built with API tokens, this is how the auth header is constructed. Migrate to OAuth before April 30, 2027.
1. Enable token access: Admin Center > Apps and Integrations > APIs > API Tokens > Enable 2. Create a token and copy it. 3. Auth header format: Username: your_email@company.com/token Password: your_api_token In the Authorization header (Basic Auth): Authorization: Basic BASE64(email/token:your_token) Example (not encoded): admin@acme.com/token:6wIJBWbGkBMo1mRDMuVwkw1EPsNkeUj95PIz2akv 4. The GET request is the same — just swap the Authorization header. GET https://acme.zendesk.com/api/v2/search.json?query=type:ticket+requester:email IMPORTANT: This stops working April 30, 2027. Migrate to OAuth 2.0 (Step 1-2 above) before that date.
| Symptom | Likely cause | Fix |
|---|---|---|
| 401 on token request | Wrong Client ID or Client Secret | Copy values exactly from Admin Center. Check that token access is enabled (Apps and Integrations > APIs > API Tokens). |
| 403 Forbidden | OAuth scope insufficient | Ensure scope=read is included in the token request. For ticket creation, add scope=write or scope=read+write. |
| Empty results array | No open tickets for that email, or email mismatch | Try removing status:open from the query to see all tickets. Check that the email matches exactly what is stored in Zendesk. |
| 422 Unprocessable on ticket create | Requester email not a valid Zendesk user | Zendesk auto-creates end users from email. Ensure the email format is valid. |
| Rate limit 429 | Exceeded plan limit (200/min Team, 700/min Enterprise) | Add retry logic. Check X-Rate-Limit-Remaining and Retry-After headers. |
| 'client_credentials not supported' | OAuth client type not set to Confidential | In Admin Center > OAuth Clients, ensure the client type is set to Confidential (not Public). |
Freshdesk support API: Token token= auth, ticket creation and lookup.
Read guide →Free trial, no credit card required. And if you ever get stuck, we are the only platform in India that answers you live on WhatsApp.