Answer Zendesk tickets with a drafted reply
The goal: stop your support team writing the same answer for the fifth time today. A ticket arrives, a flow reads it, looks up whatever facts the answer depends on, and leaves a drafted reply as an internal note on the ticket. An agent reads the draft, edits it if needed, and sends it. Nothing reaches the customer that a person didn’t approve.
That last part is the design, not a limitation. Routario’s Zendesk connector can post an internal note and nothing else — there is deliberately no send-a-public-reply step, so a bad draft is an internal embarrassment rather than a customer-facing one.
Step 1 — Connect your Zendesk
Section titled “Step 1 — Connect your Zendesk”Go to Settings → Connections → Apps and open the Support & Helpdesk section, then pick Zendesk.
Routario connects using an OAuth client, not an API token. That’s a deliberate choice rather than a preference: Zendesk is retiring API tokens — accounts lose the ability to create new ones on 27 October 2026, and every remaining token stops working on 30 April 2027. A Zendesk trial created today already has no “add token” button, so tokens couldn’t onboard you even now.
In Zendesk, go to Admin Center → Apps and integrations → APIs → OAuth clients and create a client of kind Confidential. Then paste into Routario:
- Subdomain — the
yourcompanypart ofyourcompany.zendesk.com. - Client ID — Zendesk calls this the client’s unique identifier.
- Client Secret — generated when you create the client, and shown once.
- OAuth scope — optional. Leave it blank and Routario asks for what the
connector needs:
tickets:read tickets:write users:read.
Click Connect. Routario fetches a token and reads your account back as a check. Your client secret is stored encrypted and never shown again.
Access tokens from this kind of client last 30 minutes and can’t be refreshed, so Routario just requests a fresh one when the old one expires. You’ll never see this happen; it’s worth knowing only because it means there’s no “token expired” state for you to fix.
Step 2 — Send tickets into Routario
Section titled “Step 2 — Send tickets into Routario”Connecting lets Routario call Zendesk. To have Zendesk tell Routario when something happens, you need one of two things: a webhook, or a scheduled poll. The webhook is better if you can create it. The poll exists for when you can’t.
Option 1 — a webhook (instant, and catches replies)
Section titled “Option 1 — a webhook (instant, and catches replies)”In Zendesk, create a webhook pointing at:
https://<your-routario-address>/zendesk/webhook/ticketZendesk shows a signing secret once, when the webhook is created. Copy it into the Webhook signing secret field on the Zendesk connection page in Routario. Deliveries that aren’t signed with it are rejected — so until you paste it, the trigger accepts nothing at all.
Option 2 — poll on a schedule (nothing to set up in Zendesk)
Section titled “Option 2 — poll on a schedule (nothing to set up in Zendesk)”Creating that webhook needs somebody with admin rights in your Zendesk. If that person doesn’t exist, or won’t do it, your ticket flows don’t have to wait.
Build a second, tiny flow alongside the real one:
- A Schedule (Cron) trigger, every 5 to 15 minutes.
- One step: Poll Zendesk tickets.
That’s the entire flow. Each run asks Zendesk which tickets were opened recently and sends each one down the same path a webhook delivery takes — same values read back from Zendesk, same tag matching, same protection against handling a ticket twice. Your support flow can’t tell the difference.
Two settings, both optional:
- Minutes to look back — default 30, allowed 5 to 1440. Make it comfortably larger than your schedule interval. With a 10-minute schedule, a 30-minute window means each ticket is seen about three times, and that overlap costs you nothing: a ticket already handled at its current state is recognised and skipped. A window smaller than the interval leaves gaps where a ticket is opened and never seen at all. After an outage, widen it (up to a day) to sweep up what was missed.
- Max tickets per run — default 50, allowed 1 to 500. A safety cap on how much one run can set off. If a run hits it, the next one picks up the rest.
Each run reports what it did, which is worth a look on the Logs page for the first few:
- Fetched (
count_fetched) — everything Zendesk returned for the window. This is normally higher than the number handled, because Zendesk also hands back old tickets that were merely edited into the window. That gap is the new-tickets-only filter doing its job, not a fault. - Ingested (
count_ingested) — the newly opened tickets that actually fired flows. - Skipped (
count_skipped) — tickets already handled, by an earlier poll or by the webhook. - Errors (
count_errors) — tickets that couldn’t be read. The run carries on past them and the next poll tries again, so a couple here isn’t a failed run.
Either way: add the Zendesk ticket trigger
Section titled “Either way: add the Zendesk ticket trigger”Now add a Zendesk ticket trigger to your flow. It gives you the ticket as proper named values, ready to use anywhere in the flow:
ticket_id, subject, description, status, requester_id,
assignee_id, tags, custom_fields, created_at, updated_at,
ticket_url — plus payload, the raw delivered body, for anything the named
values don’t carry.
Two behaviours worth knowing:
- Every value is read back from Zendesk, not taken from the delivered message. So what your flow sees is what Zendesk holds right now — and a forged delivery can’t inject values into your flow.
- Retries won’t re-run your flow. Zendesk retries until it gets a success, and one ticket change can fan out through several Zendesk triggers. Routario recognises a repeat of the same ticket state and ignores it, while a genuine later change — a new comment — fires the flow again.
The trigger has one optional setting, Match tag: only fire when one of the
ticket’s tags matches a pattern like fitment or shipping*. Use it to give
each support topic its own flow instead of one flow branching on everything.
Step 3 — Read what you need off the ticket
Section titled “Step 3 — Read what you need off the ticket”Five read steps, each answering a different question.
Read Zendesk ticket
Section titled “Read Zendesk ticket”Reads one ticket’s fields plus its whole comment thread, oldest first. Each comment carries who wrote it (staff or customer), whether the customer could see it, the text, the time, and whether it had a photo attached — including one pasted straight into the body of an e-mail rather than added as a file. That last part just tells you a photo exists; add Get Zendesk ticket attachments to actually fetch it.
Two values are worked out for you, because nearly every flow wanted them and had to loop to get them:
last_staff_public_reply— the most recent answer your team actually sent the customer.first_customer_message— what they originally asked.
That first one is the basis of a learning loop: draft a reply, wait for your team to answer for real, then compare the two and see where the draft fell short.
This is also the step to use when a flow is started by hand rather than by a webhook — a manual run has only a ticket number, and this turns that number into the full conversation.
Read the customer’s turn on a Zendesk ticket
Section titled “Read the customer’s turn on a Zendesk ticket”The step to put first in a flow that drafts replies. Before anything is written it settles the questions every such flow gets wrong at least once:
- Is there anything to answer?
actionisdraftwhen the customer is waiting,no_actionwhen your team already replied after them (or no customer wrote at all), andhandledwhen Routario already left a draft for this message. Zendesk fires your flow again on every reply, so switch onactionand lethandledend the run quietly. A Regenerate caller passesforce: regeneratedto draft again anyway. - Which messages are open?
open_messagesholds every customer message since your team’s last public reply — a customer who asks one thing and then writes again about another gets both answered, not just the newest. - Who wrote what? The customer is told apart from your team by who wrote a
comment, not by whether it’s public: customers sometimes reply on a private
comment. A customer’s e-mail forwarded through one of your own mailboxes is
unwrapped and counted as theirs, and
customer.emailis then the forwarded sender, not your mailbox. - What did they actually write?
turn_textis their own words with the quoted mail underneath cut away.timeline_mdis the whole conversation, one short line per message with who and when, ready to hand to a prompt. - Whom do we greet?
customer.first_namecomes from how they signed, then your own earlier mail they quoted back, then their profile, then afirst.lastaddress — and stays empty when none of those is certain, because “Hi,” is always safe and a wrong name never is. List the names your team signs with in Our names (staff_names), so a colleague’s name quoted back is never read as the customer’s.
It also returns the intake form’s custom fields by name (form['order_id'])
and, when the ticket follows up a closed one, puts the last messages of that
earlier ticket at the top of the timeline — so you don’t need a second
Read Zendesk ticket step for it. Use turn_key as the note’s idempotency
key, so one customer message gets one draft.
Read Zendesk ticket fields by name
Section titled “Read Zendesk ticket fields by name”Reads the ticket’s custom fields keyed by their human name — bike_model,
order_id — instead of the numeric id Zendesk assigns them.
This step exists to prevent one specific, nasty failure. Custom-field ids are
per-instance: the field called “Bike model” might be 900002 in your test
Zendesk and something completely different in your production one. A flow that
hardcodes the number keeps running perfectly after you swap instances and
silently reads nothing — no error, just blank values, and a draft written
from facts that aren’t there.
Read through this step and reference fields['bike_model'], and swapping test
Zendesk for production becomes a credential change rather than a flow rewrite.
Get Zendesk ticket attachments
Section titled “Get Zendesk ticket attachments”Downloads the files attached to a ticket so a later step can actually look at them — the photo of the part, the screenshot of the error.
Zendesk hangs attachments off comments rather than off the ticket, so this
walks every comment. A photo sent in a follow-up reply is found, not just one
attached to the first message — and so is one pasted straight into the body
of an e-mail, which is how most customers actually send a photo and which
Zendesk otherwise omits entirely. You can filter by type — image/ to ignore
PDFs and other documents — and cap how many files come back.
A thread usually carries e-mail furniture too: the signature logo on every message, a social-media icon. To keep only the photos the customer actually took, set three more fields:
- Smallest file (bytes)
20000— a logo is a few kilobytes, a phone photo hundreds. - Skip names containing
logo— catches a large banner the size floor lets through. - Newest only
3— the latest photos, which are usually the ones the question is about.
Every file these leave out is listed in skipped with the reason, so a photo
dropped by a wrong threshold is visible rather than missing.
To have the photos read, pass {{ photos.attachments }} (for a step named
photos) to the Image(s) field of an Ask AI
step on a vision-capable model. Its Images field decides the shape of the
answer: together gives one answer about the set (the same part from three
angles), and each gives one description per photo in descriptions. In both
modes text holds something you can put straight into a drafter’s prompt.
Post Zendesk internal note
Section titled “Post Zendesk internal note”The write-back, and the only step in the connector that changes anything.
Give it a ticket number and the text of the draft. The note lands on the ticket as an internal note — visible to your agents, invisible to the customer. There is no public/private toggle to get wrong: a public reply is a separate capability that this connector deliberately does not have.
It’s also safe to re-run. If a flow retries, the note isn’t posted twice.
Turn on Keep blank lines on paste (paste_safe) when the note is a reply
your agents will copy into the reply box. Zendesk’s composer drops the blank
lines between a note’s paragraphs when it’s pasted, so a well-spaced draft
arrives as one solid block. With this on, each gap is written as a line holding
a non-breaking space — invisible in the note, a visible empty line after the
paste. Links keep working.
A worked example: the support desk
Section titled “A worked example: the support desk”Putting the pieces together, a support flow reads:
- Zendesk ticket trigger fires on a new ticket or a customer reply.
- Read the customer’s turn on a Zendesk ticket decides whether there is
anything to draft, and hands every later step the open messages, the
greeting name and the intake form’s values — the topic, the order number if
they gave one. A Switch on its
actionends the run when the answer is no. - Branch on the topic, so a shipping question and a fitment question take different routes.
- On the shipping route, Find WooCommerce order looks the order up in the shop, so the answer is written from the real order rather than around it. (See Answer order and stock questions from your shop.)
- Ask AI drafts the reply, given the ticket and the facts from step 4.
- Post Zendesk internal note leaves the draft on the ticket.
An agent opens the ticket, finds the answer already written and grounded in real data, and sends it. The slow part — reading the history, looking things up in another system, composing — is done.
When deliveries don’t arrive
Section titled “When deliveries don’t arrive”The inbound half — Zendesk calling Routario — is the part most likely to need a look, and it fails closed: an unverified delivery is rejected rather than processed. If your flow isn’t firing, work down this list.
- No signing secret saved. Without it every delivery is refused. It’s shown once in Zendesk when the webhook is created; if you didn’t copy it, create a new webhook to get a new one.
- The webhook only listens for ticket created. Follow-ups update an existing ticket, so they never arrive. Add updated.
- The tag filter doesn’t match. Match tag compares against the tags Zendesk holds on the ticket — not against the ticket form or topic name. If the intake form sets a field rather than a tag, leave Match tag empty and branch on the field inside the flow instead.
Routario records why a delivery was rejected, so the Logs page will tell you which of these it was rather than leaving you guessing.
If you’re polling instead of using a webhook, none of the three above can be your problem — there’s no signing secret and no delivery to refuse. Check these instead:
- The customer replied rather than opening a new ticket. Polling only fires on newly opened tickets; see Polling fires on new tickets, not on replies above. This is the single most common surprise.
- The lookback window is shorter than the schedule interval. Tickets opened in the gap between one run’s window and the next are never looked at. The window should be several times the interval, not equal to it.
- The polling flow itself isn’t running. It’s an ordinary scheduled flow — check it’s switched on and that its runs appear on the Logs page. A run that reports tickets fetched but none ingested is usually the reply case above, not a broken poll.
Where to go next
Section titled “Where to go next”- Answer order and stock questions from your shop — the lookups that make a support draft factual rather than plausible.
- Keep a human in the loop — the wider pattern this connector is built around.
- Template variables — how to reference the trigger’s
values, like
{{ subject }}and{{ ticket_id }}, further down the flow. - See what happened — Logs — where to read back what a poll fetched, ingested and skipped.