Skip to content

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.

Further reading#