Zendesk Ticket Comments Over the API

Reading Zendesk ticket comments and writing them use different endpoints, and nobody expects that. Here's the whole model, including notes, attachments and privacy.

Zendesk ticket comments aren't a field on the ticket

Fetch a ticket and you get its fields, its IDs and a description. The description is the text of the very first comment, and nothing else. The rest of the conversation isn't in that object.

This catches everybody. Somebody builds a sync, ships it, and a week later discovers they have been storing opening lines rather than conversations.

To read the thread, ask for the comments.

curl -u "$ZD_USER/token:$ZD_TOKEN" \
  "$ZD_URL/api/v2/tickets/35436/comments.json"

You get an ordered array. Each comment carries an id, an author_id, a created_at, a body in plain text, an html_body, a public flag and any attachments. Add ?include=users to get the authors in the same response rather than resolving each ID separately.

Long threads paginate, like everything else. Use cursor pagination and follow the links rather than assuming a fourteen-message ticket fits in one page, because some of them don't.

The asymmetry: you write through the ticket

There's no POST endpoint for creating a comment. You update the ticket and include a comment object in the payload.

PUT /api/v2/tickets/35436.json
{
  "ticket": {
    "comment": {
      "body": "Refund processed, it lands in three working days.",
      "public": true
    }
  }
}

Read from one place, write to another. That's the single most common reason a first integration doesn't work, and searching the reference for "create comment" doesn't find it, because the operation is called updating a ticket.

The upside is that one call does everything. Add the comment, change the status, reassign, set a field, all in the same update. Rather than four requests you send one, which is both faster and atomic.

Public replies and internal notes

The public flag is the whole distinction, and getting it wrong is the failure mode with actual consequences.

`public: true` is a reply to the customer. It goes out by email or into the messaging thread, and it's visible in the help centre if they have an account.
`public: false` is an internal note. Agents and light agents see it, the requester does not.

Default behaviour depends on the endpoint and on who's authenticating, so set the flag explicitly on every single write. Never rely on the default. An automated diagnostic dump posted publicly because somebody assumed notes were the default is a bad afternoon and, occasionally, a data protection incident.

There is an endpoint for making an existing comment private, which is the closest thing to an undo. It doesn't unsend the email that already went out. Nothing does.

Also worth knowing: author_id can be set on a comment where your credentials allow it, which is how an integration posts on behalf of an agent rather than as itself. Without it, every note in the account is authored by your bot.

Attachments, formatting and redaction

Attachments

You upload first and attach second. Send the file to the uploads endpoint, get back a token, then include that token in the uploads array on your comment. Multiple files can share a token. Tokens expire, so upload as part of the same operation rather than hours earlier.

Reading goes the other way: comments carry attachment metadata and authenticated URLs, not the bytes. Downloading means fetching each URL with credentials.

Formatting

Comment bodies support a limited set of HTML, and Zendesk sanitises what it does not like. Send html_body when you need formatting, body when you do not, and test what survives rather than assuming your markup came through. Long code blocks and complex tables generally do not.

Redaction

Comments are part of the permanent record, and editing one isn't a normal operation. Zendesk provides redaction for removing sensitive strings from a comment, which is what you use when a customer pastes a card number. It is destructive and it is auditable, which is exactly the right trade.

Events, audits and the full history

Comments are really one kind of ticket event. Underneath, every change to a ticket, a field update, a status change, a comment, is recorded as an audit.

That's why the audits endpoint shows more than the comments endpoint. If your question is "who changed the priority and when", comments won't answer it and audits will. If your question is "what did we say to the customer", comments is the cleaner read.

For bulk work, the incremental ticket events endpoint gives you everything that changed since a timestamp, comments included, which is the right way to keep an external copy of conversations in sync. Walking every ticket and fetching its comments individually works at small scale and falls over at large scale, and there is a rate limit waiting for you either way.

One last quirk, and it is a real one: when tickets are merged in Zendesk, only the most recent public comment carries across to the target ticket. The full thread stays on the closed source ticket. Any pipeline reading conversations needs to expect that.

FAQ

Frequently asked questions

How do you post an internal note rather than a public reply?

Set public to false on the comment. The Zendesk internal note API and the Zendesk public reply API are the same endpoint with that one flag, and the Zendesk ticket conversation API for reading them is a different path entirely.

How do I add Zendesk ticket comments through the API?

Update the ticket with a PUT and include a comment object containing a body and a public flag. There's no endpoint for creating a comment directly.

How do I add an internal note instead of a reply?

Set public: false on the comment. Set the flag explicitly every time rather than relying on a default, because defaults vary by endpoint and by authenticating user.

Why does a ticket response not contain the conversation?

Because comments are a separate resource. The ticket object carries description, which is only the first comment. List the comments endpoint for the rest.

Can I edit or delete a comment?

Not as a normal update. Zendesk offers redaction for removing sensitive content, and an endpoint for making an existing comment private, but neither unsends an email already delivered.

How do I attach a file to a comment?

Upload it to the uploads endpoint first, take the returned token, then include that token in the uploads array on the comment you write.

Two tickets, two threads, one problem

Ticket Merger merges duplicate Zendesk tickets on requester, subject keywords and ticket fields inside a time window, before the conversation splits in two.

Start free trial

14-day free trial. No credit card required.