Zendesk + MailKite
Zendesk sends ticket notifications, auto-replies, and agent replies through its own email infrastructure. Route those through MailKite to send from your own DKIM-signed domain — customers see your brand, not Zendesk's.
What you need
- A verified domain with SPF + DKIM published
- Your API key (
mk_live_…) - Zendesk Support (any plan)
Configure outbound email
In Zendesk Admin, go to Channels → Email and edit your support address. Change the SMTP settings to use MailKite:
# Zendesk Support — Admin → Channels → Email
# Outbound email SMTP settings
SMTP server: smtp.mailkite.dev
Port: 587
Username: mailkite
Password: mk_live_...
Authentication: PLAIN (over TLS)
TLS: Yes (STARTTLS)
Set the From name to your brand and the From
address to an address on your verified domain (e.g.
support@yourdomain.com).
What gets sent through MailKite
- Ticket auto-replies — "We got your request" emails
- Agent replies — responses to customer issues
- Internal note notifications — agent-to-agent alerts
- Satisfaction surveys — CSAT/NPS emails after resolution
- Trigger-based emails — any custom trigger that sends email
Receive inbound email → Zendesk tickets
Zendesk already has a unique inbound address per ticket (e.g.
support+ticketid@yourdomain.zendesk.com). If you use MailKite
for inbound, you can pipe replies back into Zendesk via the API:
// Zendesk -> MailKite: send ticket replies via your domain
// Uses Zendesk's Ticket API to add a public comment
export default {
async fetch(request, env) {
if (request.method !== "POST") return new Response("OK");
const payload = await request.json();
const { from, subject, text, inReplyTo } = payload;
// Extract ticket ID from subject or headers
// Zendesk includes ticket ID in the subject: "[Ticket #1234] Re: ..."
let ticketId;
if (inReplyTo) {
// Parse ticket ID from the In-Reply-To header if available
const match = inReplyTo.match(/ticket[_-]?(\d+)/i);
if (match) ticketId = match[1];
}
if (!ticketId) {
const match = subject.match(/#(\d+)/);
if (match) ticketId = match[1];
}
if (!ticketId) return new Response('{"ok":true}', { status: 200 });
// Add comment to the ticket
const res = await fetch(
"https://" + env.ZENDESK_SUBDOMAIN + ".zendesk.com/api/v2/tickets/" + ticketId + ".json",
{
method: "PUT",
headers: {
Authorization: "Bearer " + env.ZENDESK_API_TOKEN,
"Content-Type": "application/json",
},
body: JSON.stringify({
ticket: {
comment: {
body: "From: " + from + "\n\n" + text,
public: false,
},
},
}),
}
);
return new Response(JSON.stringify({ updated: res.ok }), { status: 200 });
},
}; Set your domain's webhook to this endpoint. Customer replies are added as internal comments on the matching ticket.
Multiple brands
Zendesk supports multiple brands with different email addresses. Point each
brand's From address at a verified MailKite domain — they all
share the same API key and logs.
Troubleshooting
- Outbound going to spam — confirm SPF + DKIM are published on your sending domain. Zendesk may add its own headers; disable Zendesk's built-in DKIM in Admin → Email → DKIM if you see conflicts.
- Ticket ID not parsing — Zendesk's subject format varies by trigger. The webhook handler above handles common patterns; adjust the regex if your Zendesk uses a different format.
- Authentication failed — the password must be your
mk_live_…API key, not a Zendesk credential. - Missing inbound replies — ensure your domain's MX record points to
mx.mailkite.devand the webhook is set.
See the SMTP relay docs for connection details, or Inbound webhooks for the full payload.