The Zendesk Create Ticket API, Without the Duplicates
The Zendesk create ticket API is one POST request. Creating exactly one ticket, attributed to the right person, is the part that takes a week to get right.
The minimum Zendesk create ticket API payload
A POST to /api/v2/tickets.json with a body wrapped in a ticket object. The only genuinely required part is a first comment.
In practice that means a subject, a comment with a body, and a requester carrying a name and an email address. Three keys and you have a ticket.
Everything else is optional and everything else is where the behaviour lives: group_id, assignee_id, type, custom_fields, external_id, ticket_form_id on plans with multiple forms.
The response hands back the created ticket including its ID. Store that ID against whatever record in your system prompted the creation. Not in a log line, in a proper column you can query. Almost every duplicate problem downstream starts with somebody not having done this.
Getting the requester right
By default, a ticket created with API credentials is attributed to the account those credentials belong to. Do nothing about the requester and every ticket your integration creates will appear to have been raised by "Integration Bot", which makes the customer invisible in reporting and means your reply notifications go to nobody useful.
There are two ways to fix it.
That second form is convenient and it is also how you end up with three users for one human. ada@example.com and ada.bell@example.com are different people as far as Zendesk is concerned. If your source system has a canonical email, use that one every time rather than whichever address happened to be on this record.
There is also submitter_id, which records who filed the ticket as opposed to who it's about. Use it when a colleague raises something on somebody else behalf, and set the requester to the person with the problem. It's the difference between a ticket the affected customer can see and one they can't.
Notifications, triggers and quiet creation
A ticket created over the API is a normal ticket. Triggers fire, the "we received your request" autoresponder goes out, SLAs start. Usually that is what you want.
Sometimes it is emphatically not. Backfilling a hundred historical tickets with the standard create endpoint will email a hundred customers about problems they reported last year. Zendesk has a separate ticket import endpoint for exactly this: original timestamps, a full comment history, and no triggers or notifications. Use it for anything historical.
To keep specific rules out of the way on live tickets, apply a tag at creation and exclude it in your trigger conditions. You can exclude the integration user instead, but the tag is easier to reason about six months later when you've forgotten which user the integration authenticates as.
Creating tickets from Slack
Slack is the most common non-API way an API ticket gets created, since the Zendesk Slack integration and most homegrown bots do exactly this under the hood: take a message, POST a ticket, post the ticket link back into the thread.
Three decisions make or break it.
Internal helpdesks running this pattern reliably see the same incident raised several times over, because four helpful colleagues each turn the same outage message into a ticket. It is not a bug in the integration. It's what happens when you make raising a ticket a single click for people who are trying to help.
The duplicate problem, and what actually prevents it
Creation has no natural idempotency. Send the same POST twice and you get two tickets, identical, seconds apart. And you'll send it twice, because networks time out after the server has already done the work, because queues retry, because a webhook consumer got redelivered, because somebody double-clicked.
Use external_id
Set external_id to a stable identifier from your own system: the order number, the alert fingerprint, the row ID. It costs nothing at creation and gives you something to search on and reconcile against later.
Record before you create
Write your intent to create a ticket to your own store first, with a unique key, then create, then write back the Zendesk ID. If the process dies mid-flight you can tell the difference between "never created" and "created but the response was lost". Relying on a search to work that out afterwards fails because of index lag.
Prefer updating to creating
If your integration can reasonably attach a new event to an existing open ticket, do that instead. A comment on the right ticket beats a second ticket about the same thing every time.
None of this catches the customer who emails and then raises a ticket through the portal an hour later, or two colleagues reporting one outage from different addresses. Those are duplicates no idempotency key can see.
Frequently asked questions
Which endpoint creates a new ticket?
POST to /api/v2/tickets.json with a ticket object. That single Zendesk API new ticket call covers subject, comment, requester, tags and custom fields, and everything else is a follow-up update.
How do I set the requester on the Zendesk create ticket API?
Pass requester_id if you know the user ID, or a requester object with a name and email, which matches on the email address and creates the user if none exists.
Will API-created tickets fire triggers and send autoresponders?
Yes. If you want silence, for example when backfilling history, use the ticket import endpoint instead, which sets original timestamps and skips notifications.
How do I stop retries creating duplicate tickets?
Set external_id from your own system, record the intent to create before you call the API, and check your own store rather than searching Zendesk, since the search index lags.
Can I create a ticket with several comments at once?
Not with the standard create endpoint, which takes one comment. The import endpoint accepts a full comment history with original timestamps.
Why do tickets from Slack show the bot as the customer?
Because no requester was mapped. Resolve the Slack profile email to a Zendesk user and set it as the requester, with the raiser as the submitter where they differ.
Two tickets, seconds apart, identical
Ticket Merger catches the pairs your integration created on a retry, and the ones a human created because they did not know the first ticket existed.
Start free trial14-day free trial. No credit card required.