Helpdesk / Ticketing System#
Problem statement (interviewer prompt)
Design a customer support ticketing system like Zendesk, Intercom, or Jira Service Desk: ingest issues from email, chat, web form, and APIs; route to the right agent by skill / load / SLA; thread updates; meet response-time SLAs.
flowchart TB
Email[Email] --> Ingest
Chat[Chat widget] --> Ingest
Web[Web form] --> Ingest
API[API] --> Ingest
Ingest[Ingest gateway] --> Tckt[(Ticket store)]
Ingest --> Class[Auto-categorise / NLP]
Class --> Router[SLA + skill router]
Router --> Agent[Agent inbox]
Tckt --> Notify[Notify subscribers]
Tckt --> SLA[SLA tracker]
SLA -->|breach risk| Alert
classDef client fill:#dbeafe,stroke:#1e40af,stroke-width:1px,color:#0f172a;
classDef edge fill:#cffafe,stroke:#0e7490,stroke-width:1px,color:#0f172a;
classDef service fill:#fef3c7,stroke:#92400e,stroke-width:1px,color:#0f172a;
classDef datastore fill:#fee2e2,stroke:#991b1b,stroke-width:1px,color:#0f172a;
classDef cache fill:#fed7aa,stroke:#9a3412,stroke-width:1px,color:#0f172a;
classDef queue fill:#ede9fe,stroke:#5b21b6,stroke-width:1px,color:#0f172a;
classDef compute fill:#d1fae5,stroke:#065f46,stroke-width:1px,color:#0f172a;
classDef storage fill:#e5e7eb,stroke:#374151,stroke-width:1px,color:#0f172a;
classDef external fill:#fce7f3,stroke:#9d174d,stroke-width:1px,color:#0f172a;
classDef obs fill:#f3e8ff,stroke:#6b21a8,stroke-width:1px,color:#0f172a;
class Email,Chat,Web,API client;
class Ingest edge;
class Class,Router,Notify,SLA,Alert,Agent service;
class Tckt datastore;
Tickets are append-only conversation threads; SLAs track first-response and resolution times; routing is the trickiest piece in practice.
A ticketing platform unifies multi-channel customer issues into a queue of work for support agents, with SLAs and historical context.
Ingest channels#
flowchart LR
Email[IMAP / Gmail / Postmark]
Chat[Live chat widget]
Web[Web form]
Slack[Slack / Teams integration]
API[Public API]
Social[Twitter / Facebook]
Email --> Norm[Normaliser: extract from+subject+body]
Chat --> Norm
Web --> Norm
Slack --> Norm
API --> Norm
Social --> Norm
Norm --> Thread{Existing thread<br/>same sender + subject?}
Thread -->|yes| Append[Append message]
Thread -->|no| Create[Create new ticket]
classDef client fill:#dbeafe,stroke:#1e40af,stroke-width:1px,color:#0f172a;
classDef edge fill:#cffafe,stroke:#0e7490,stroke-width:1px,color:#0f172a;
classDef service fill:#fef3c7,stroke:#92400e,stroke-width:1px,color:#0f172a;
classDef datastore fill:#fee2e2,stroke:#991b1b,stroke-width:1px,color:#0f172a;
classDef cache fill:#fed7aa,stroke:#9a3412,stroke-width:1px,color:#0f172a;
classDef queue fill:#ede9fe,stroke:#5b21b6,stroke-width:1px,color:#0f172a;
classDef compute fill:#d1fae5,stroke:#065f46,stroke-width:1px,color:#0f172a;
classDef storage fill:#e5e7eb,stroke:#374151,stroke-width:1px,color:#0f172a;
classDef external fill:#fce7f3,stroke:#9d174d,stroke-width:1px,color:#0f172a;
classDef obs fill:#f3e8ff,stroke:#6b21a8,stroke-width:1px,color:#0f172a;
class Email,Chat,Web,Slack,API,Social client;
class Norm,Append,Create service;
class Thread edge;
Ticket data model#
Ticket
id, organisation_id, customer_id, status, priority, channel, created_at
subject, tags[], custom_fields
assignee_id, group_id
sla_first_response_due_at, sla_resolved_due_at
metrics: first_reply_at, resolved_at
Message
ticket_id, sender (customer/agent), body, attachments[], internal_note: bool, created_at
Audit
ticket_id, field, from, to, by, at
Routing#
Rules engine evaluated on every new ticket:
if ticket.tags ⊇ {"billing"}:
group = "billing"
if customer.tier == "enterprise" and priority == "high":
sla = "P1-15min"
assignee = least_busy_agent_in(group, skills=ticket.predicted_skills)
Combine with ML auto-tagging (intent classification, NER) to populate tags and predicted skills.
SLA tracking#
sequenceDiagram
participant T as Ticket
participant Clock as SLA tracker
participant Alert
T->>Clock: created at 10:00
Note over Clock: first-response SLA = 1h
loop every minute
Clock->>Clock: check tickets nearing breach
end
Clock->>Alert: 30min remaining, no agent reply -> escalate
Clock->>Alert: breach -> page on-call
Business hours, pauses while waiting on customer reply, and per-tenant SLA policies all complicate the math.
Knowledge base + automation#
flowchart LR
New[New ticket] --> Suggest[Suggest KB articles]
Suggest -. self-serve .-> Customer[Customer reads, resolves]
Suggest --> Agent[Agent uses as canned reply]
classDef client fill:#dbeafe,stroke:#1e40af,stroke-width:1px,color:#0f172a;
classDef edge fill:#cffafe,stroke:#0e7490,stroke-width:1px,color:#0f172a;
classDef service fill:#fef3c7,stroke:#92400e,stroke-width:1px,color:#0f172a;
classDef datastore fill:#fee2e2,stroke:#991b1b,stroke-width:1px,color:#0f172a;
classDef cache fill:#fed7aa,stroke:#9a3412,stroke-width:1px,color:#0f172a;
classDef queue fill:#ede9fe,stroke:#5b21b6,stroke-width:1px,color:#0f172a;
classDef compute fill:#d1fae5,stroke:#065f46,stroke-width:1px,color:#0f172a;
classDef storage fill:#e5e7eb,stroke:#374151,stroke-width:1px,color:#0f172a;
classDef external fill:#fce7f3,stroke:#9d174d,stroke-width:1px,color:#0f172a;
classDef obs fill:#f3e8ff,stroke:#6b21a8,stroke-width:1px,color:#0f172a;
class Suggest,Agent service;
class Customer client;
class New datastore;
Modern systems use RAG (vector + KB) to draft replies; agent reviews and sends.
Reporting#
- Agent productivity (tickets resolved, response time)
- Channel mix
- SLA compliance per tier
- CSAT (customer satisfaction score from post-ticket surveys)
- Backlog age
Multi-tenant isolation#
Each customer organisation is a tenant. Per-tenant SLAs, branding, custom fields, role assignments.
Where helpdesk fits#
flowchart TB
HD((Helpdesk /<br/>ticketing))
MT[Multi-tenancy<br/>per-tenant SLAs]
NOT[Notification system<br/>outbound to agent + customer]
RAG[Vector search / RAG<br/>KB-augmented assist]
EMAIL[Email service<br/>inbound + outbound mail]
MT --> HD
HD --> NOT
RAG --> HD
EMAIL --> HD
classDef client fill:#dbeafe,stroke:#1e40af,stroke-width:1px,color:#0f172a;
classDef edge fill:#cffafe,stroke:#0e7490,stroke-width:1px,color:#0f172a;
classDef service fill:#fef3c7,stroke:#92400e,stroke-width:1px,color:#0f172a;
classDef datastore fill:#fee2e2,stroke:#991b1b,stroke-width:1px,color:#0f172a;
classDef cache fill:#fed7aa,stroke:#9a3412,stroke-width:1px,color:#0f172a;
classDef queue fill:#ede9fe,stroke:#5b21b6,stroke-width:1px,color:#0f172a;
classDef compute fill:#d1fae5,stroke:#065f46,stroke-width:1px,color:#0f172a;
classDef storage fill:#e5e7eb,stroke:#374151,stroke-width:1px,color:#0f172a;
classDef external fill:#fce7f3,stroke:#9d174d,stroke-width:1px,color:#0f172a;
classDef obs fill:#f3e8ff,stroke:#6b21a8,stroke-width:1px,color:#0f172a;
class HD service;
class MT,NOT,RAG,EMAIL datastore;
Glossary & fundamentals#
| Tag | Concept | What it is | Page |
|---|---|---|---|
HLD |
Multi-tenancy patterns | the tenant model | multi-tenancy |
HLD |
Notification system | outbound to agent + customer | notification-system |
HLD |
Vector search / RAG | KB-augmented agent assist | vector-search-rag |
HLD |
Email service | the inbound + outbound mail leg | email-service |
Quick reference#
Channels#
Email, chat, web form, Slack/Teams, API, social.
Ticket model#
Ticket (status, priority, assignee, SLA) + Messages (customer / agent / internal note) + Audit.
Routing#
Rules engine + ML auto-tagging → group → least-busy agent matching skills.
SLA tracking#
First-response and resolution SLAs; per-tier policy; business hours; pause when waiting customer.
KB + automation#
RAG to draft replies; agent reviews and sends. Self-serve KB suggested at ticket creation.
Reporting#
Resolved/agent, response time, SLA compliance, CSAT, backlog age.
Multi-tenant#
Per-tenant SLAs, branding, custom fields, RBAC, ACL.
Capacity sketch#
1000 tenants × 100 tickets/day = 100k tickets/day. Reads dominate (agent views).
Tools#
Zendesk, Intercom, Freshdesk, Jira Service Desk, HubSpot Service Hub.
Refs#
- Zendesk dev docs
- Intercom engineering blog
FAQ#
How do you design a ticketing system like Zendesk?#
Ingest from email, chat, and APIs into a normalized ticket store. Run NLP for category and intent, route by skill and load, track SLAs in a separate service, and notify agents via push.
How is ticket routing implemented?#
A router service consumes new tickets, looks up agent skills and current load, and assigns to the best match. Round-robin within skill keeps load even; SLA priority can preempt assignment.
How are email threads grouped into one ticket?#
Use the In-Reply-To and References headers when present, else hash subject + sender + a recency window. Each match appends to the existing ticket; otherwise create a new one.
How is the SLA tracker built?#
An SLA service holds per-ticket deadlines in a sorted structure (heap or scheduled queue) and fires events on breach risk and breach. Breaches escalate to a manager or auto-reassign.
How do agents collaborate on a ticket?#
Internal notes are stored on the ticket but hidden from the customer. Mentions notify the named agent, and a watchers list subscribes others to email or in-app updates.
Related Topics#
- Multi-Tenancy Patterns: per-tenant SLAs and branding
- Notification System: outbound to agents and customers
- Vector Search / RAG: the KB-augmented agent assist
Further reading#
- Doc - Zendesk developer docs
- Doc - Intercom architecture posts