Field Notes.

The Browser Is the API: How I Built zd-agent-cli

Cover Image for The Browser Is the API: How I Built zd-agent-cli
Roger Rodriguez
Roger Rodriguez

Listen to this post

AI-generated narration of The Browser Is the API: How I Built zd-agent-cli.

A while back, I had one of those very normal enterprise moments.

We had a clear opportunity to use AI agents for ticket triage. The technical path looked straightforward. Then the non-code work showed up: credentials, ownership, rotation, approvals, controls, timelines.

By the time we would finish all that, the support queue would still be waiting.

That is why I built zd-agent-cli.

Not because Zendesk APIs are bad. They are useful. But for this use case, I needed a faster path that still respected our security model.

The bet was simple: the browser session is already authenticated, already governed, and already trusted. So treat the browser as the integration layer.

The actual problem: triage drag

Our technical support teams were not blocked on effort. They were blocked on throughput.

Triage is repetitive by design:

  1. Open queue
  2. Open ticket
  3. Parse subject, status, assignee, requester, latest comments
  4. Classify urgency and route
  5. Repeat

Do that a few hundred times and the bottleneck is obvious. Humans are great at judgment, bad at repetitive extraction loops. AI agents can help, but only if they get clean context quickly and consistently.

So the business target was clear:

  • Speed up first-pass triage
  • Improve consistency in routing decisions
  • Free support engineers to solve issues instead of sorting them

The approach: use existing auth, output structured data

zd-agent-cli connects to a locally authenticated Zendesk Agent Workspace session through Chrome DevTools Protocol (CDP), then returns structured JSON.

No default API key flow. No extra auth surface for the pilot.

That was the adoption unlock. We could move fast while staying inside existing identity and access controls.

If the logged-in agent can access a queue, the CLI can read it. If they cannot, it cannot. Same permissions, same boundaries.

What the CLI does

At the command line, the interface is intentionally boring:

zagent --json doctor
zagent --json auth check
zagent --json queue list
zagent --json queue read support-open --count 20
zagent --json ticket read 123456 --comments 10
zagent --json search tickets "checkout issue" --count 20

Under the hood, the flow is opinionated:

  1. Resolve config from CLI, env, and config file
  2. Validate the contract early (domain, startPath, queue aliases)
  3. Attach to CDP (or launch a profile if needed)
  4. Read via API-first paths where possible
  5. Fall back to DOM reads when API paths are thin
  6. Emit normalized JSON for downstream agents and automations

That API-first with DOM fallback split made the tool much more reliable in real usage.

The engineering pain points that mattered most

CDP ownership checks

The fastest way to lose trust in a tool is cross-profile weirdness.

If a CDP endpoint was live, I could not just grab it blindly. I added profile ownership checks around --user-data-dir so the CLI verifies it is talking to the expected Chrome profile.

If there is a mismatch, it can:

  • Reuse a matching owned endpoint in a scanned port range
  • Launch a new Chrome profile on a free port
  • Fail with a precise error

This is not flashy engineering. It is operational hygiene. It prevents the exact class of bugs that burn time during incidents.

Auto-port fallback

Hardcoding one CDP port works until the day it does not.

I added automatic port scanning and fallback so users are less likely to hit dead ends before they even run their first command.

Cache and replay modes

Ticket reads support TTL caching and --cache-only.

That ended up being very useful for agent loops that repeatedly revisit the same ticket set. Less browser churn, faster iterations, and deterministic local replay when debugging.

A doctor command that actually helps

I wanted setup failures to be obvious.

zagent doctor checks config, contract validity, CDP reachability, profile ownership, and Zendesk auth. Instead of "it broke", you get a short list of exactly what is wrong.

In support operations, faster diagnosis is part of reliability.

Why this translated to business impact

Each technical choice mapped to a practical support outcome:

  • Reusing browser auth lowered rollout friction for AI-assisted triage
  • Structured JSON made it easier to plug into agent workflows
  • Reliability features reduced run failures during active queue work
  • Local-first operation helped with trust, governance, and pilot velocity

Net effect: teams could start using AI agents to help classify and route tickets sooner, with less process drag and less tool babysitting.

What to measure if you roll out something similar

If you want to prove impact, track before and after on these:

  • Time to first triage decision
  • Tickets triaged per hour
  • Percent of tickets pre-classified before deep human review
  • Queue aging for high-priority tickets

Even a short measurement window can show whether the workflow is improving.

Lessons learned

  1. Adoption beats architectural purity in early stages.
  2. Reliability work is product work.
  3. Clean interfaces beat clever ones when multiple teams are involved.
  4. Small tools can unlock large behavior changes.

What I would build next

The next step is not adding fifty new commands. It is improving decision quality:

  • Better enrichment and categorization signals
  • Queue-specific triage heuristics
  • Stronger eval loops for agent output quality and drift

The goal stays the same: less time sorting, more time solving.

That is the story of zd-agent-cli. A practical tool built to remove friction between support teams and useful AI assistance.