← Resources/GuideCustomer Support

Customer Support Platform Architecture: How IdeaSprout Handles Scale, Routing & Data Integrity

1 April 2026·14 min read

Overview

A production-grade customer support platform is far more complex than a shared inbox. It must ingest messages from heterogeneous channels, normalise them into a unified ticket model, route them intelligently, enforce SLA contracts in real time, and maintain a tamper-proof audit trail - all while staying responsive under burst load.

This guide walks through the architectural decisions behind IdeaSprout's Customer Support module.


1. Multi-Channel Ingestion Layer

The Problem

Customers contact you via email, WhatsApp, web chat, phone (voice-to-text), and social DMs. Each channel has a different payload format, authentication model, and delivery guarantee.

Architecture Pattern: Channel Adapters + Normalisation Queue

Each channel has a dedicated adapter service responsible for:

  • •Authenticating inbound webhooks (HMAC signature verification for WhatsApp Business API, DKIM for email)
  • •Parsing the raw payload into a canonical InboundMessage schema
  • •Publishing to a central ingestion queue (Apache Kafka topic: raw.inbound.messages)
WhatsApp Webhook → WhatsApp Adapter → Kafka: raw.inbound.messages
Email (IMAP/SMTP) → Email Adapter  → Kafka: raw.inbound.messages
Web Chat (WS)     → Chat Adapter   → Kafka: raw.inbound.messages

Canonical InboundMessage Schema

json
{
  "messageId": "uuid-v4",
  "channel": "whatsapp | email | chat | phone",
  "customerId": "resolved or null",
  "contactIdentifier": "+91-XXXXXXXXXX | [email protected]",
  "body": "plain text content",
  "attachments": [],
  "receivedAt": "ISO-8601",
  "rawPayload": "base64-encoded original"
}

Key Design Decisions

  • •Idempotency: Each adapter deduplicates using messageId before publishing. WhatsApp re-delivers webhooks on timeout - without dedup, you get duplicate tickets.
  • •At-least-once delivery: Kafka guarantees at-least-once. The ticket creation consumer is idempotent (upsert on messageId).
  • •Customer resolution: A separate CustomerResolver service enriches the message with customerId by looking up the contact identifier against the CRM. If unresolved, the ticket is created with customerId: null and flagged for manual association.

2. Ticket Creation & Deduplication Engine

Thread Detection

Replies to existing tickets must be appended to the thread, not create new tickets. The engine uses:

  1. 1Email: In-Reply-To and References headers → match against ticket.emailThreadId
  2. 2WhatsApp: context.id field in the webhook payload → match against ticket.whatsappThreadId
  3. 3Fallback: Fuzzy match on customerId + recency window (last open ticket within 24h from same customer)

Ticket State Machine

NEW → OPEN → PENDING (awaiting customer) → RESOLVED → CLOSED
         ↓
      ESCALATED → OPEN (reassigned)

State transitions are enforced server-side. Invalid transitions (e.g., CLOSED → OPEN without explicit reopen) are rejected with a 422.


3. Intelligent Routing Engine

Routing Rules Evaluation Order

  1. 1Keyword rules - regex match on ticket body (e.g., "refund" → Billing queue)
  2. 2Channel rules - WhatsApp tickets → WhatsApp-trained agents
  3. 3Customer tier rules - Enterprise customers → Senior agents
  4. 4Round-robin - within the matched queue, distribute evenly by agent load score

Agent Load Score

load_score = (open_tickets × 1.0) + (pending_tickets × 0.3) + (escalated_tickets × 2.0)

The agent with the lowest load score in the target queue receives the ticket. Scores are recomputed on every assignment event.

Overflow Handling

If all agents in a queue exceed max_load_score (configurable, default: 15), the ticket enters a backlog queue and triggers a Slack/email alert to the queue manager.


4. SLA Engine

The Core Challenge

SLA timers must pause when the ball is in the customer's court (ticket in PENDING state) and resume when the customer replies. This requires business-hours-aware elapsed time calculation.

Timer Architecture

Each ticket has two SLA clocks:

  • •First Response Time (FRT): Time from ticket creation to first agent reply
  • •Resolution Time (RT): Total agent-active time from creation to RESOLVED

Both clocks are stored as cumulative elapsed seconds, not as absolute deadlines. This makes timezone changes and business-hours reconfiguration non-destructive.

sql
CREATE TABLE sla_events (
  ticket_id    UUID,
  event_type   ENUM('start', 'pause', 'resume', 'breach'),
  occurred_at  TIMESTAMPTZ,
  elapsed_ms   BIGINT  -- cumulative at this point
);

Breach Detection

A scheduled job runs every 60 seconds and evaluates:

current_elapsed = last_elapsed + (now - last_resume_at) × business_hours_factor
if current_elapsed > sla_threshold → emit SLA_BREACH event

SLA breach events trigger escalation rules (reassign, notify manager, change priority).


5. CSAT Collection Pipeline

Trigger

CSAT surveys are sent 2 hours after a ticket moves to CLOSED state (configurable delay). The survey is sent via the same channel the customer used.

Anti-Spam Guard

  • •Maximum 1 CSAT survey per customer per 7 days
  • •If a customer has multiple tickets closed in the same window, only the most recent triggers a survey

Score Aggregation

CSAT scores are written to a time-series table partitioned by (product_id, date). The dashboard queries a materialised view that recomputes rolling 7-day and 30-day averages on a 15-minute schedule - avoiding expensive full-table scans on every dashboard load.


6. Audit Trail & Data Integrity

Append-Only Event Log

Every state change, assignment, reply, and SLA event is written to an append-only event log table. The current ticket state is derived by replaying events (event sourcing pattern) or read from a denormalised snapshot table that is updated by event consumers.

This means:

  • •You can reconstruct the full history of any ticket at any point in time
  • •Deleted or edited messages are detectable (the original is preserved in the log)
  • •Compliance audits can be satisfied by exporting the event log

Data Retention

  • •Active tickets: hot storage (PostgreSQL)
  • •Closed tickets > 90 days: cold storage (S3 + Parquet), queryable via Athena
  • •Event log: retained for 7 years (regulatory requirement for customer communications in India)

7. Common Architectural Pitfalls

PitfallConsequenceFix
Storing SLA as absolute deadlineTimezone bugs, business-hours changes break all open ticketsStore as elapsed seconds
No deduplication on webhook ingestionDuplicate tickets from WhatsApp re-deliveryIdempotent upsert on messageId
Routing computed at query timeDashboard slowness under loadPre-compute load scores on assignment events
CSAT sent immediately on closeCustomer hasn't processed resolution yet, scores are lower2-hour delay
Mutable ticket bodyAudit trail gaps, compliance riskAppend-only message log

Summary

A robust customer support platform requires careful design at every layer: idempotent ingestion, thread-aware deduplication, load-balanced routing, business-hours-aware SLA timers, and an append-only audit trail. Cutting corners on any of these leads to duplicate tickets, SLA miscalculations, or compliance failures that are expensive to fix retroactively.

architectureticketing systemSLA enginemulti-channelscalability