Connectors
/
Zendesk
Live · 14 tools

Zendesk Integration for AI Agents

The Zendesk API is well-documented. Getting your agent to act on real customers' tickets — correctly, across multiple support instances, without leaking one tenant's credentials into another — is the part nobody warns you about.
Zendesk
Live

Customer Support

Help Desk

Status
Live
Tools
14 pre-built
Auth
API Key
Credential storage
Sandbox support

API Key Auth

Per-user isolation

Subdomain routing

Ticket + user tools

The real problem

Why this is harder than it looks

Zendesk's REST API is clean and the documentation is thorough. You can build a working single-account prototype in an hour. The complexity arrives when you try to support real users in a multi-tenant product — and it's different from most connectors because Zendesk doesn't use OAuth 2.0 by default.

Zendesk authenticates via API token — a credential that is account-scoped, not user-scoped. That means a single token can impersonate any verified user on the entire Zendesk instance. In a multi-tenant product where each of your customers has their own Zendesk subdomain, you're now managing a credential vault: one (subdomain + email + API token) triple per customer, each pointing to a different base URL. There's no OAuth redirect flow to standardize the collection of these credentials — you have to build a UI that prompts each customer for their subdomain and token, store those credentials encrypted, and route every API call to the right subdomain. Get the routing wrong and you'll make API calls against the wrong customer's instance with no helpful error message.

Beyond credential collection, the operational problems compound: tokens don't expire, which sounds convenient until a customer rotates their Zendesk API token and your calls start returning 401s with no automated recovery path. You need revocation detection, re-credentialing flows, and per-account status tracking — all without the OAuth lifecycle hooks that other connectors provide. And if a customer has IP allowlists configured in their Zendesk Admin Center, your server IPs need to be explicitly permitted or calls will fail silently after successful credential setup.

Scalekit handles credential storage, per-tenant subdomain routing, and account status tracking. Your agent names a tool and passes parameters. The plumbing is not your problem.

Capabilities

What your agent can do with Zendesk

Once connected, your agent has 14 pre-built tools covering the Zendesk support workflow end-to-end:

  • Create, read, and update tickets: open new tickets with priority, type, and assignee; fetch ticket details with sideloaded data; update status, routing, and tags
  • Search and list tickets: full Zendesk search syntax with sorting and pagination; list tickets from any view or queue
  • Reply to tickets and add internal notes: post public replies or agent-only internal notes to any ticket by ID
  • Read ticket comments: fetch the full conversation thread including attachments and author metadata
  • Manage users and organizations: create and look up end-users, agents, and admins; list and retrieve organizations
  • Inspect groups and views: list agent groups and saved ticket views to understand routing and queue structure
Setup context

What we're building

This guide connects a support assistant agent to Zendesk — helping agents triage tickets, post replies, escalate issues, and look up customer records without leaving your product.

🤖
Example agent
Support assistant triaging tickets, posting replies, and managing Zendesk queues on behalf of each support rep
🔐
Auth model
API Key — each customer supplies their Zendesk subdomain, email, and API token. identifier = your user ID
⚙️
Scalekit account
app.scalekit.com — Client ID, Secret, Env URL
🐍
Runtime
Python 3.8+ · Node.js also available
Setup

1 Setup: One SDK, One credential

Install the Scalekit SDK. The only credential your application manages is the Scalekit API key — no Zendesk secrets, no user tokens, nothing belonging to your customers.

pip install scalekit-sdk-python
npm install @scalekit-sdk/node
import scalekit.client import os from dotenv import load_dotenv load_dotenv() scalekit = scalekit.client.ScalekitClient( client_id=os.getenv("SCALEKIT_CLIENT_ID"), client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), env_url=os.getenv("SCALEKIT_ENV_URL"), ) actions = scalekit.actions
import { ScalekitClient } from '@scalekit-sdk/node'; import 'dotenv/config'; const scalekit = new ScalekitClient( process.env.SCALEKIT_ENV_URL, process.env.SCALEKIT_CLIENT_ID, process.env.SCALEKIT_CLIENT_SECRET ); const actions = scalekit.actions;
Already have credentials?
Connected Accounts

2 Per-User Auth: Creating connected accounts

Zendesk uses API Key authentication — there is no OAuth redirect flow. Each user's connected account is provisioned directly with their Zendesk subdomain, email address, and API token. The identifier is any unique string from your system.

In the Scalekit dashboard, go to Agent Auth → Connected Accounts for your Zendesk connection and click Add account. Supply the user's identifier, their Zendesk domain (e.g. yourcompany.zendesk.com), email address, and API token. Scalekit stores the credentials encrypted and routes every API call to the correct subdomain automatically.

response = actions.get_or_create_connected_account( connection_name="zendesk", identifier="user_zd_123" # your internal user ID ) connected_account = response.connected_account print(f"Status: {connected_account.status}") # Status: ACTIVE — credentials were supplied at account creation
const response = await actions.getOrCreateConnectedAccount({ connectionName: "zendesk", identifier: "user_zd_123" // your internal user ID }); const connectedAccount = response.connectedAccount; console.log(`Status: ${connectedAccount.status}`); // Status: ACTIVE — credentials were supplied at account creation

This call is idempotent — safe to call on every session start. Returns the existing account if one already exists.

Credential handling

3 Credential management

Because Zendesk uses API keys rather than OAuth, there is no user-facing authorization step. Credentials are entered once in the Scalekit dashboard (or via API) per connected account. Scalekit stores them encrypted and uses them on every tool call.

Credential storage is automatic
Once a connected account is provisioned with Zendesk credentials, Scalekit stores them in its encrypted vault and the account is immediately ACTIVE. Every tool call is routed to the correct Zendesk subdomain using the stored credentials — your agent code never touches them. If credentials become invalid (e.g. the customer rotates their API token), the account moves to REVOKED. Check account.status before critical operations and surface a re-credentialing prompt.
Generating a Zendesk API token
In Zendesk Admin Center, go to Apps and integrations → APIs → Zendesk API. Under Settings, enable Token access, then click Add API token. Copy the token immediately — it is only shown once. The token authenticates as the email address supplied alongside it, so use an account with the appropriate permissions for the operations your agent will perform.
Calling Zendesk

4 Calling Zendesk: What your agent writes

With the connected account active, your agent calls Zendesk actions using execute_tool. Name the tool, pass parameters. Scalekit handles credential retrieval, subdomain routing, and response parsing.

Search tickets

Use Zendesk's native search syntax — the same strings you'd type in the Zendesk search bar. Filter by status, assignee, tag, date, or any ticket property. Returns up to 1000 results with pagination.

result = actions.execute_tool( identifier="user_zd_123", tool_name="zendesk_search_tickets", tool_input={ "query": "type:ticket status:open assignee:me priority:urgent", "sort_by": "updated_at", "sort_order": "desc", "per_page": 25 } ) # Returns list of matching tickets with id, subject, status, priority, assignee
const result = await actions.executeTool({ identifier: "user_zd_123", toolName: "zendesk_search_tickets", toolInput: { "query": "type:ticket status:open assignee:me priority:urgent", "sort_by": "updated_at", "sort_order": "desc", "per_page": 25 } }); // Returns list of matching tickets with id, subject, status, priority, assignee

Create a ticket

Open a new support ticket. Only comment_body is required — all other fields are optional and can be set at creation or updated later.

result = actions.execute_tool( identifier="user_zd_123", tool_name="zendesk_ticket_create", tool_input={ "subject": "Login broken after SSO change", "comment_body": "Customer reports they cannot log in after the SSO migration on March 28. Affects 3 accounts.", "priority": "high", "type": "problem", "assignee_email": "tier2@support.example.com", "tags": ["sso", "login", "escalated"] } ) # Returns: { "id": 9821, "subject": "...", "status": "new", ... }
const result = await actions.executeTool({ identifier: "user_zd_123", toolName: "zendesk_ticket_create", toolInput: { "subject": "Login broken after SSO change", "comment_body": "Customer reports they cannot log in after the SSO migration on March 28. Affects 3 accounts.", "priority": "high", "type": "problem", "assignee_email": "tier2@support.example.com", "tags": ["sso", "login", "escalated"] } }); // Returns: { "id": 9821, "subject": "...", "status": "new", ... }

Reply to a ticket

Post a public reply or internal note. Set public to false for internal notes visible only to agents — useful when your agent is summarizing context or escalation notes before a human takes over.

# Public reply to the customer result = actions.execute_tool( identifier="user_zd_123", tool_name="zendesk_ticket_reply", tool_input={ "ticket_id": 9821, "body": "Thanks for reaching out. Our team is investigating and will follow up within 2 hours.", "public": True } ) # Internal note for the agent queue result = actions.execute_tool( identifier="user_zd_123", tool_name="zendesk_ticket_reply", tool_input={ "ticket_id": 9821, "body": "Escalated to Tier 2. Root cause likely the SSO config change deployed March 28 — see incident #4401.", "public": False } )
// Public reply to the customer const reply = await actions.executeTool({ identifier: "user_zd_123", toolName: "zendesk_ticket_reply", toolInput: { "ticket_id": 9821, "body": "Thanks for reaching out. Our team is investigating and will follow up within 2 hours.", "public": true } }); // Internal note for the agent queue const note = await actions.executeTool({ identifier: "user_zd_123", toolName: "zendesk_ticket_reply", toolInput: { "ticket_id": 9821, "body": "Escalated to Tier 2. Root cause likely the SSO config change deployed March 28 — see incident #4401.", "public": false } });

Update a ticket

Change ticket status, reassign, update priority, or apply tags in a single call. Only the fields supplied are changed — all others remain untouched.

result = actions.execute_tool( identifier="user_zd_123", tool_name="zendesk_ticket_update", tool_input={ "ticket_id": 9821, "status": "pending", "priority": "urgent", "assignee_email": "oncall@support.example.com", "tags": ["sso", "login", "escalated", "p1"] } )
const result = await actions.executeTool({ identifier: "user_zd_123", toolName: "zendesk_ticket_update", toolInput: { "ticket_id": 9821, "status": "pending", "priority": "urgent", "assignee_email": "oncall@support.example.com", "tags": ["sso", "login", "escalated", "p1"] } });
Framework wiring

5 Wiring into your agent framework

Scalekit integrates directly with LangChain. The agent decides what to look up or act on; Scalekit handles credential routing on every invocation. No credential plumbing in your agent logic.

from langchain_anthropic import ChatAnthropic from langchain.agents import AgentExecutor, create_tool_calling_agent from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder from scalekit.langchain import get_tools zd_tools = get_tools( connection_name="zendesk", identifier="user_zd_123" ) prompt = ChatPromptTemplate.from_messages([ ("system", "You are a support assistant. Use the available tools to help manage Zendesk tickets, reply to customers, and look up user and organization information."), MessagesPlaceholder("chat_history", optional=True), ("human", "{input}"), MessagesPlaceholder("agent_scratchpad"), ]) agent = create_tool_calling_agent(ChatAnthropic(model="claude-sonnet-4-6"), zd_tools, prompt) result = AgentExecutor(agent=agent, tools=zd_tools).invoke({ "input": "Find all urgent open tickets assigned to me, summarize them, and add an internal note to each flagging the SLA deadline" })
import { ChatAnthropic } from "@langchain/anthropic"; import { AgentExecutor, createToolCallingAgent } from "langchain/agents"; import { ChatPromptTemplate, MessagesPlaceholder } from "@langchain/core/prompts"; import { getTools } from "@scalekit-sdk/langchain"; const zdTools = getTools({ connectionName: "zendesk", identifier: "user_zd_123" }); const prompt = ChatPromptTemplate.fromMessages([ ["system", "You are a support assistant. Use the available tools to help manage Zendesk tickets, reply to customers, and look up user and organization information."], new MessagesPlaceholder("chat_history", true), ["human", "{input}"], new MessagesPlaceholder("agent_scratchpad"), ]); const agent = await createToolCallingAgent({ llm: new ChatAnthropic({ model: "claude-sonnet-4-6" }), tools: zdTools, prompt }); const result = await AgentExecutor.fromAgentAndTools({ agent, tools: zdTools }).invoke({ input: "Find all urgent open tickets assigned to me, summarize them, and add an internal note to each flagging the SLA deadline" });
Other frameworks supported
Tool reference

All 14 Zendesk tools

Grouped by object and capability. Your agent calls tools by name — no API wrappers to write.

Tickets
zendesk_ticket_create
Create a new ticket with subject, description, priority (urgent/high/normal/low), type, assignee email, status, and tags
zendesk_ticket_get
Retrieve a specific ticket by ID. Supports sideloading related users, groups, and organizations in one request
zendesk_ticket_update
Update any writable ticket field — status, priority, assignee, group, subject, or tags. Only supplied fields are changed
zendesk_ticket_reply
Post a public reply or internal note to a ticket. Set public to false for agent-only notes
zendesk_ticket_comments_list
List all comments and internal notes on a ticket including body, author, timestamps, and attachments
zendesk_tickets_list
List tickets with sorting by created_at, updated_at, priority, or status. Supports pagination
zendesk_search_tickets
Search tickets using Zendesk's full search syntax — filter by assignee, tag, date, status, type, and more. Returns up to 1000 results
Users
zendesk_user_create
Create a new user (end-user, agent, or admin) with name, email, phone, organization association, and verified status
zendesk_user_get
Retrieve a specific user by ID including name, email, role, organization, and account status
zendesk_users_list
List users filtered by role (end-user, agent, admin) with sorting and pagination
Organizations
zendesk_organization_get
Retrieve a specific organization by ID including name, domain names, tags, notes, and shared ticket settings
zendesk_organizations_list
List all organizations in the account with pagination support
Groups & Views
zendesk_groups_list
List all agent groups in the account — used for understanding ticket routing and queue structure
zendesk_views_list
List saved ticket views filtered by access level (personal, shared, account) with sorting and pagination
Connector notes

Zendesk-specific behavior

API tokens are account-scoped, not user-scoped
A Zendesk API token paired with an email address authenticates as that user and can perform any action that user is permitted to do. Unlike OAuth tokens, API tokens do not expire automatically — but they can be manually revoked or rotated in Zendesk Admin Center. If a customer rotates their token, the connected account will move to REVOKED. Surface a re-credentialing flow rather than returning a generic error.
Every customer has a different subdomain
Zendesk API calls go to https://{subdomain}.zendesk.com/api/v2/. Each of your customers operates on their own subdomain — there is no shared base URL. Scalekit routes each tool call to the correct subdomain based on the credentials stored for that connected account. Ensure the domain entered at account setup is the full Zendesk domain, not a custom or host-mapped subdomain.
tags on zendesk_ticket_update replace, not append
The tags field in zendesk_ticket_update replaces the ticket's entire tag set. To add a tag without removing existing ones, first fetch the current tags with zendesk_ticket_get, merge your new tag into the list, and pass the full merged array to the update call.
Infrastructure decision

Why not build this yourself

The Zendesk API is documented. Credential storage isn't technically hard. But here's what you're actually signing up for:

PROBLEM 01
Per-customer subdomain routing — every API call must go to the right customer's Zendesk instance, with the right credentials, with no shared base URL
PROBLEM 02
API tokens don't expire but they do get rotated — you need revocation detection and re-credentialing flows without the OAuth lifecycle hooks other connectors provide
PROBLEM 03
Per-user credential isolation in a multi-tenant system — one customer's Zendesk token must never route calls to another customer's instance
PROBLEM 04
Encrypted credential storage, account status tracking, and a UI for collecting customer API tokens at onboarding — all before you've written a single ticket operation

That's one connector. Your agent product will eventually need Salesforce, Gmail, GitHub, Linear, and whatever else your customers ask for. Each has its own auth quirks and failure modes.

Scalekit maintains every connector. You maintain none of them.

Ready to ship

Connect your agent to Zendesk today

Free to start. Subdomain routing handled.
Zendesk
Live

Customer Support

Help Desk

Status
Live
Tools
14 pre-built
Auth
API Key
Credential storage
Sandbox support