Skip to content
Skip to main content

Connect Zendesk to email with Nylas

Last updated:

Zendesk’s native email connector ties tickets to one support address per brand. If you also need to act on a shared mailbox you don’t host in Zendesk, a connected sales inbox, or a Microsoft 365 group, you have to bridge the gap yourself. This recipe wires inbound mail straight into the Zendesk Tickets API: a customer writes in, a ticket appears, and an agent’s reply goes back out through the same mailbox the customer wrote to.

The whole loop runs on two Nylas primitives: a message.created webhook for the inbound side and the send endpoint for the outbound side. Zendesk’s POST /api/v2/tickets does the ticket creation in between.

A Zendesk email integration built on Nylas has two halves. Inbound: a message.created webhook fires when mail arrives, your handler maps the message to a ticket, and POST /api/v2/tickets creates it. Outbound: when an agent replies in Zendesk, you send that reply with reply_to_message_id set, so it threads correctly in the customer’s client.

customer email ─▶ message.created webhook ─▶ map fields ─▶ POST /api/v2/tickets
agent reply in Zendesk ─▶ your handler ─▶ messages/send (reply_to_message_id) ─▶ customer

Each half is independent. If the outbound path breaks, tickets still get created; the inbound path keeps running. All 6 providers (Google, Microsoft, Yahoo, iCloud, IMAP, and EWS) flow through the same code because the Email API returns one message schema across every connected account, and the message.created webhook delivers up to 1 MB of message data per notification.

Why route through Nylas instead of Zendesk’s email connector?

Section titled “Why route through Nylas instead of Zendesk’s email connector?”

Zendesk’s built-in connector forwards or polls a single support address and converts those messages into tickets. The Email API gives you the raw message object and full send control over any connected mailbox across all 6 providers, which matters in three cases.

  • You want tickets from a mailbox Zendesk doesn’t own, like a Microsoft 365 shared mailbox or a sales@ alias, without forwarding rules that strip headers.
  • You need the message.created payload (headers, reply_to, thread_id) to drive routing logic before a ticket exists.
  • You want replies to thread in the customer’s mail client using reply_to_message_id, not arrive as a fresh email.

If your only inbox is a single Zendesk-hosted support address, the native Zendesk email setup is simpler and you don’t need this. The bridge earns its keep once mail lives outside Zendesk.

The inbound half starts with a webhook subscription on the message.created trigger, which Nylas fires within seconds of mail landing in any connected grant. The request below registers your HTTPS endpoint and returns a webhook_secret you use to verify every notification. One subscription covers all grants in your application.

Nylas verifies the endpoint with a one-time challenge query parameter that your server must echo back in a 200 response within 10 seconds. For the signature verification handler and challenge logic, follow the monitor an inbox for support tickets recipe, then return here for the Zendesk-specific mapping. Don’t duplicate the handler; it’s the same plumbing.

This is the core of the integration. Zendesk’s create-ticket call is POST /api/v2/tickets, and it expects a requester object, a subject, and a comment whose body becomes the ticket’s first message. You map 5 message fields onto that shape, and the call returns a 201 status on success. The table below is the contract; everything else is glue.

Nylas message fieldZendesk ticket fieldNotes
from[0].email / from[0].namerequester.email / requester.nameZendesk creates the user if they don’t exist
subjectsubjectFalls back to “(no subject)” when empty
body (or snippet)comment.bodyBecomes the first public comment
idexternal_idLinks the ticket to the Nylas message
date(custom field, optional)Unix seconds; convert to ISO for display

The handler below takes the delivered message object, builds the ticket payload, and posts it. Zendesk returns 201 Created with a ticket.id you should store alongside the Nylas message ID so the outbound path can find both later.

The external_id field is the load-bearing piece. It lets you query Zendesk later with GET /api/v2/tickets?external_id=<message_id> to check whether a ticket already exists, which keeps duplicate webhook deliveries from creating two tickets for one email. See Zendesk’s Tickets API reference for the full field list.

Thread an agent reply back to the customer

Section titled “Thread an agent reply back to the customer”

When an agent answers in Zendesk, the customer should get a normal email reply in the same conversation, not a detached message. You send the reply through POST /v3/grants/{grant_id}/messages/send and set reply_to_message_id to the original message’s ID. Nylas adds the right In-Reply-To and References headers so the reply threads in Gmail, Outlook, and standard IMAP clients.

The send request below uses the same mailbox the customer wrote to, so the From address stays consistent across all 6 providers. Set reply_to_message_id to the Nylas message ID you stored as the ticket’s external_id, and to to the original sender. Sent mail lands in the mailbox’s Sent folder like any other reply.

To trigger this from Zendesk rather than your own code, point a Zendesk webhook and trigger at a small endpoint that calls send_reply. Pass the comment body and the stored Nylas message ID from the ticket’s external_id. For the reverse direction (logging the outbound reply back onto the ticket), use Zendesk’s PUT /api/v2/tickets/{ticket_id} with a comment body.

Before you flip on the live webhook, you’ll usually want to import the support mail already sitting in the mailbox. List recent messages with GET /v3/grants/{grant_id}/messages, which returns up to 200 messages per page, then run each one through the same create_ticket function. This gives Zendesk a starting history instead of an empty queue on day one.

The request below pulls the 50 most recent messages from the connected mailbox. Filter to the support address with the to query parameter, or narrow by time with received_after so you don’t re-import years of archives. Pass the external_id check before creating each ticket to skip mail already ticketed.

A few details decide whether this integration stays clean or fills Zendesk with duplicates and orphaned threads. Most of them come from how Zendesk and email behave, not from Nylas.

  • Dedupe on external_id, every time. Nylas guarantees at-least-once webhook delivery, so the same message.created can arrive twice. Query GET /api/v2/tickets?external_id=<message_id> before creating, or rely on the fact that a second create with the same external_id is a strong signal to skip. One email should never become two tickets.
  • Watch the 1 MB truncation. When a message exceeds 1 MB (large attachments, heavy HTML), Nylas strips the body and adds the .truncated suffix. Detect the missing body and refetch the full message before you set comment.body, or your ticket opens with an empty description.
  • Don’t ticket your own outbound. When an agent reply sends, it also fires message.created for the connected mailbox. Check the folders array for SENT and skip those, or you’ll create a ticket every time your team answers one.
  • Zendesk auth uses an API token, not a password. Authenticate as {email}/token with the token as the secret. Tokens are managed in the Zendesk Admin Center under Apps and integrations.
  • requester creates users silently. If the sender’s email isn’t a known Zendesk user, the create call provisions one. That’s usually what you want for support, but it means a typo’d or spoofed From address becomes a real user record. Validate the sender domain before creating tickets from untrusted inboxes.
  • HTML bodies need handling. Nylas returns the message body as HTML. Zendesk accepts HTML in comment.html_body, so use that field instead of comment.body when you want formatting preserved rather than escaped.