Zendesk CLI (zd)
An unofficial, agent-friendly command-line interface for Zendesk's ticketing REST API. Built for both humans and AI agents.
What it does
zd lets you manage Zendesk tickets from the terminal. List, search, create, update, and delete tickets with structured output that works well in scripts and AI agent workflows. It includes built-in discovery commands (zd commands and zd schema) that let AI agents introspect the CLI at runtime, so they can figure out what's available without hardcoded knowledge.
Installation
Homebrew
brew install johanviberg/tap/zd
go install
go install github.com/johanviberg/zd@latest
Build from source
git clone https://github.com/johanviberg/zd.git
cd zd
go build -o zd
Authentication
API token
zd auth login --method token \
--subdomain mycompany \
--email you@example.com \
--api-token YOUR_API_TOKEN
Setting up an OAuth client in Zendesk
Before using the OAuth flow, you need to register an OAuth client in Zendesk:
- In Admin Center, go to Apps and integrations → APIs → OAuth clients, then click Add OAuth client.
- Fill in the fields:
- Name — e.g.
zd CLI (shown to users on the consent screen)
- Description — optional
- Client kind — select Confidential (the CLI runs locally and stores the secret with restricted file permissions)
- Redirect URLs — enter
http://127.0.0.1/callback (the CLI starts a local server on a random port; Zendesk matches on host and path, ignoring the port for localhost)
- Click Save. A Secret field appears — copy it immediately, it is only shown in full once.
- Note the Identifier field — this is the Client ID.
Use the Identifier as --client-id and the Secret as --client-secret in the command below.
OAuth (browser flow)
zd auth login \
--subdomain mycompany \
--client-id YOUR_CLIENT_ID \
--client-secret YOUR_CLIENT_SECRET
This opens a browser window for the OAuth consent flow. The CLI requests read write scopes. The token is stored locally.
Environment variables
You can skip auth login entirely by setting environment variables:
# API token auth
export ZENDESK_SUBDOMAIN=mycompany
export ZENDESK_EMAIL=you@example.com
export ZENDESK_API_TOKEN=your_token
# Or OAuth token
export ZENDESK_SUBDOMAIN=mycompany
export ZENDESK_OAUTH_TOKEN=your_oauth_token
Check auth status
zd auth status
Quick start
# List recent tickets
zd tickets list
# Show a specific ticket
zd tickets show 12345
# Create a ticket
zd tickets create --subject "Printer broken" --comment "The office printer is not responding"
# Update a ticket
zd tickets update 12345 --status pending --comment "Waiting on vendor" --public=false
# Search tickets
zd tickets search "status:open priority:high"
# Delete a ticket (requires confirmation)
zd tickets delete 12345 --yes
Use --output (or -o) to control how results are formatted:
# Human-readable table (default)
zd tickets list
# JSON
zd tickets list -o json
# Newline-delimited JSON (one object per line, good for piping)
zd tickets list -o ndjson
Field projection
Use --fields to select specific fields:
zd tickets list --fields id,status,subject -o json
Errors always go to stderr. When using --output json, errors are also structured JSON on stderr.
Using with AI agents
zd is designed to be used by AI agents like Claude Code. Two commands make this possible:
Command discovery
zd commands lists every available command with its flags, types, defaults, and argument names:
zd commands -o json
An agent can call this once to learn the full CLI surface.
zd schema generates a JSON Schema for any command's input, which maps directly to tool-calling conventions:
zd schema --command "tickets create"
This returns a schema with property types, required fields, and defaults that an agent can use to construct valid calls.
Example: Claude Code with CLAUDE.md
Add something like this to your project's CLAUDE.md to give Claude Code access to Zendesk:
## Zendesk
Use the `zd` CLI to interact with Zendesk. Auth is already configured.
- Run `zd commands -o json` to discover available commands
- Run `zd schema --command "<command>"` to get the input schema for a command
- Always use `--output json` when reading ticket data
- Use `--non-interactive` to prevent prompts
You can wrap zd as an MCP tool by pointing your server at the binary and using zd schema to generate input schemas dynamically.
Command reference
| Command |
Description |
zd auth login |
Authenticate with Zendesk (OAuth or API token) |
zd auth logout |
Remove stored credentials |
zd auth status |
Show current authentication status |
zd tickets list |
List tickets |
zd tickets show <id> |
Show a ticket |
zd tickets create |
Create a ticket |
zd tickets update <id> |
Update a ticket |
zd tickets delete <id> |
Delete a ticket |
zd tickets search <query> |
Search tickets using Zendesk query syntax |
zd config show |
Show current configuration |
zd config set <key> <value> |
Set a configuration value |
zd commands |
List all commands with flags (for agent discovery) |
zd schema --command "..." |
JSON Schema for a command's input |
zd version |
Print version information |
Global flags
| Flag |
Description |
-o, --output |
Output format: text, json, ndjson (default: text) |
--fields |
Field projection (comma-separated) |
--no-headers |
Omit table headers in text mode |
--non-interactive |
Never prompt for input |
--yes |
Auto-confirm prompts |
--subdomain |
Override Zendesk subdomain |
--profile |
Config profile (default: default) |
--debug |
Debug logging to stderr |
--trace-id |
Trace ID attached to API requests |
Configuration
Config files live in $XDG_CONFIG_HOME/zd/ (typically ~/.config/zd/):
config.yaml -- settings per profile
credentials.json -- stored auth tokens (file permissions: 0600)
Profiles
You can maintain multiple Zendesk accounts using profiles:
# Login to a second account
zd auth login --profile staging --subdomain mycompany-staging --method token \
--email you@example.com --api-token STAGING_TOKEN
# Use it
zd tickets list --profile staging
Setting config values
zd config set subdomain mycompany
zd config show
Exit codes
| Code |
Meaning |
| 0 |
Success |
| 1 |
General error |
| 2 |
Argument error |
| 3 |
Authentication error |
| 4 |
Retryable error (rate limited) |
| 5 |
Not found |
License
MIT