Sindh IT Portal — Facilitation DeskSpecification documents
Englishاردوسنڌي
← All documents

Multi-channel Intake

The authoritative specification for how the Sindh IT Portal — Facilitation Desk (SITP) turns any inbound contact from any channel into a tracked, routed, deduplicated, multilingual ticket — meeting companies and citizens where they already are.

Field Value
Doc ID 19
Status Draft
Owner S&ITD / MAAHIR
Languages EN (master) · UR · SD
Applies to module K — Multi-channel Intake (MCI)
Related docs /specs/en/02-functional-reqs/ (MCI FRs), /specs/en/08-integrations-spec/ (Mailjet/SMS/WhatsApp contracts), /specs/en/06-ticket-workflow/, /specs/en/15-tech-architecture/, /specs/en/05-data-model/, /specs/en/11-security-compliance/, /specs/en/24-trust-safety/
Realizes FR-MCI-001FR-MCI-007

1. Scope & Definitions

This document is the single source of truth for inbound intake across every channel the Portal supports. It defines what counts as a channel, how a message becomes a ticket (or appends to one), how the same complaint filed twice through two different channels is detected and merged, and how the system stays available even when an individual provider goes down.

The Portal's native web intake form and the partner Public REST API (POST /api/v1/tickets, see /specs/en/12-api-contract/) are the baseline intake paths and are fully specified in /specs/en/06-ticket-workflow/. This document covers the five multi-channel intake paths layered on top of that baseline, so the citizen never has to log in to a web form to be heard:

  1. Email-to-ticket
  2. SMS-to-ticket
  3. WhatsApp-to-ticket
  4. Toll-free IVR / voice
  5. Walk-in / offline entry

Every channel converges on the same ticketing core, the same SLA/escalation engine, the same AI categorization, and the same audit log. Channel is an origin attribute, never a parallel workflow.

Glossary (short form; see _glossary.md):

Term Meaning
Intake channel A transport over which an inbound contact is received and turned into a ticket or appended (web, email, SMS, WhatsApp, IVR, walk-in, API).
Inbound A message flowing from an external sender into SITP (the reverse of a notification).
Channel of origin The stored attribute on every ticket recording which channel created it (FR-MCI-006).
Thread matching Correlating an inbound reply to the ticket it concerns, by tracking ID, in-reply-to/References headers, or reply context.
Dedup Detecting that a new inbound describes the same complainant + issue as an existing open ticket, across channels.
Provisional identity A sender whose phone/email/CNIC does not yet map to a registered organization/rep; the ticket is still created and attributed.
Facilitation counter A staffed desk (S&ITD or partner department) where walk-in complaints are entered on behalf of a visitor.
Sender The person/organization on the inbound side of a message, regardless of channel.

All capabilities below are individually toggleable via the Feature Flag module (Q) and configurable per department per /specs/en/06-ticket-workflow/ §15.


2. Principle — Meet Companies Where They Are

The Portal's mandate is a single window for the IT industry to resolve problems with the Government of Sindh. A single window that only accepts submissions through a logged-in web form is not, in practice, a single window — it is a barrier. Pakistani IT companies, freelancers, and the citizens who interact with them do not live in a portal; they live in WhatsApp, SMS, email, and the phone. A field officer visiting a software park does not always have connectivity.

The Multi-channel Intake module therefore obeys four invariants:

ID Invariant What it means
MC-1 Every channel produces the same ticket. There is no "email ticket", no "WhatsApp ticket". A ticket created from WhatsApp is the same record, with the same tracking ID format, SLA, escalation ladder, proof-of-resolution gate, audit trail, and analytics dimensions as one created from the web form.
MC-2 No login required to be heard. An unregistered or unauthenticated sender can always file through an inbound channel; the system creates a Provisional identity and offers registration afterward. Authentication raises trust, not access.
MC-3 Reply in the channel of arrival. Acknowledgements, status updates, and officer messages reach the sender on the channel they used to contact the Portal, with cross-channel fallback if that channel degrades.
MC-4 Channel is data, not destiny. Channel of origin is recorded, analyzed, and audited — but never changes the rights, SLA, or routing logic of a ticket. A ticket is not "less important" because it arrived by SMS.

These invariants are referenced by ID throughout this document and in the acceptance criteria in /specs/en/02-functional-reqs/ §MCI.


3. The Intake Pipeline

3.1 Overview

Regardless of channel, every inbound message traverses the same seven-stage pipeline before becoming a ticket or a comment. The pipeline is implemented as a BullMQ worker chain; each stage is idempotent, independently retryable, and emits an audit_events row. The stages are:

  1. Ingest — receive the raw payload from the channel adapter (Mailjet inbound parse, SMS MO, WhatsApp Cloud API webhook, IVR recording callback, PWA sync batch).
  2. Normalize — extract a canonical InboundMessage (sender identifiers, raw text, attachments, locale hints, channel-specific metadata) and drop or quarantine anything that fails structural validation.
  3. PII detect & redact — run PII detection (FR-AI-009) on text and OCR-extracted attachment text; tokenize CNIC/phone/email before any cloud AI call; keep the redaction map on-prem.
  4. Dedup check — run similarity detection (FR-AI-007, FR-MCI-005) against the sender's open tickets and recent resolved tickets; surface matches for the next stage.
  5. AI categorize — classify and suggest routing (FR-AI-002, FR-AI-003); detect urgency/sentiment (FR-AI-004); detect language (§9).
  6. Create or append — either create a new ticket (FR-TKT-001 tracking ID assigned) or append as a comment to the matched existing ticket (thread matching, §7). Channel of origin is stamped (FR-MCI-006).
  7. Confirm to sender — send an acknowledgement carrying the tracking ID back through the originating channel, with cross-channel fallback (§12) if the originating channel is down.

3.2 Pipeline diagram

The diagram shows a single inbound message entering through any of the five channels and traversing the pipeline. The identity-linking and routing side-effects interact with the pipeline at the points shown. Provider outage (red dashed) triggers the fallback chain (§12) for confirmations only; ticket creation itself never depends on the originating provider.

flowchart TB subgraph Channels["Inbound channels (5)"] EMAIL["Email<br/>(Mailjet inbound parse)"] SMS["SMS<br/>(MO short-code keyword)"] WA["WhatsApp<br/>(Cloud API webhook)"] IVR["IVR / Voice<br/>(toll-free, STT)"] WALK["Walk-in / Offline<br/>(PWA sync batch)"] end subgraph Pipeline["Channel-agnostic intake pipeline (BullMQ)"] S1["1. Ingest<br/>(signature verify · replay protect)"] S2["2. Normalize<br/>→ InboundMessage"] S3["3. PII detect & redact"] S4["4. Dedup check<br/>vs open + recent tickets"] S5["5. AI categorize<br/>(category · routing · urgency · language)"] S6{"6. Create or append?"} CREATE["Create new ticket<br/>+ tracking ID + channel_of_origin"] APPEND["Append as comment<br/>to matched ticket"] end subgraph Side["Side-effects"] IDLINK["Identity linking<br/>(match phone/email/CNIC → org/rep,<br/>else Provisional)"] ROUTE["Channel-agnostic routing<br/>→ dept/section + SLA tier"] RL["Rate-limit & anti-abuse<br/>(gate at ingest)"] end OUT["7. Confirm to sender<br/>(originating channel, fallback chain)"] EMAIL --> S1 SMS --> S1 WA --> S1 IVR --> S1 WALK --> S1 RL -. gate .-> S1 S1 --> S2 --> S3 --> S4 --> S5 --> S6 IDLINK -. reads/writes identity .-> S2 S6 -->|no match| CREATE S6 -->|match open ticket| APPEND CREATE --> ROUTE APPEND -. reuses existing routing .-> ROUTE CREATE --> OUT APPEND --> OUT OUT -. provider down? .-> FALL["Fallback chain<br/>(next-best channel)"]

3.3 Written description

A message arrives on any of the five channels and is delivered to the Inbound webhook receiver described in /specs/en/08-integrations-spec/ §9.2 — a single front door that verifies the provider's signature, rejects replays, and enqueues a normalized envelope to the intake queue. The rate-limit / anti-abuse gate (§10) is applied at the receiver, before any expensive work; floods from one sender are dropped or throttled before they reach the pipeline.

The ingest worker reads the envelope, records it (sensitive fields redacted) in int_call with direction inbound, and hands the raw payload to the normalize stage, which produces a canonical InboundMessage carrying: sender identifiers (email, MSISDN, WA-verified name, CNIC if disclosed, rep ID if matched), the raw subject/body, attachments (presigned refs into the staging bucket), locale hints (Accept-Language for email, device locale for WA, detected script for SMS, language declared by IVR caller), and channel-specific metadata (message ID, reply context, in-reply-to/References headers, IVR call ID, walk-in officer ID).

PII detection runs next (FR-AI-009): CNIC, phone, email, and any configured patterns are tokenized before any cloud AI call, and a redaction map is kept on-prem so the original can be restored in storage and re-masked at presentation. Attachments are AV-scanned by ClamAV (FR-FILE-003) at this stage and quarantined on detection.

Dedup (FR-MCI-005) compares the normalized message against the sender's open tickets and recently resolved tickets using the similarity service (FR-AI-007). Matches are returned with similarity scores, not silently merged — the create-or-append decision in stage 6 consumes them.

AI categorize (FR-AI-002, FR-AI-003, FR-AI-004) produces a summary, a category, a department/section/urgency suggestion with confidence, and a sentiment label. Language detection (§9) tags the message locale. If AI is unavailable or below the confidence threshold, the ticket enters the manual triage queue without blocking.

The create-or-append decision is the only branch in the pipeline:

Finally, confirmation is sent back to the sender on the originating channel — a new ticket gets its tracking ID; an appended reply gets an acknowledgement that the message was added. If the originating provider is unavailable, the fallback chain (§12) routes the confirmation through the next-best channel for that sender (e.g., SMS fallback if WhatsApp is down).


4. Channel Capabilities (matrix)

The five inbound channels differ in bandwidth, attachment support, two-way capability, and regulatory dependencies. The table below is the capability contract every channel adapter must satisfy; the per-channel details live in §5.

Capability Email SMS WhatsApp IVR / Voice Walk-in / Offline
Channel code (channel_of_origin) email sms whatsapp ivr walkin
Transport Mailjet inbound parse MO to short-code WA Cloud API webhook Toll-free PSTN PWA sync batch
Direction Two-way (reply appends) Two-way (reply appends) Two-way (reply appends) One-way in + SMS/voice out Two-way (officer enters on behalf)
Attachment support Any (AV-scanned) None (link only) Image / doc / audio / video (AV-scanned) Voice recording (audio file) Any (AV-scanned at sync)
Message length Effectively unlimited 70–160 chars/segment (UCS-2 for UR/SD) Long text + media Time-limited recording Unlimited (form-driven)
Authentication on arrival Sender email + DKIM/SPF MSISDN (SIM-bound) WA-verified phone + opt-in MSISDN (caller ID) Officer-authenticated on device
Identity signal Email → rep/org Phone → rep/org/CNIC Phone + name → rep/org Phone → rep/org Officer-entered CNIC/org
Multilingual inbound Yes (any script in body) Yes (UCS-2 UR/SD) Yes (any script + voice notes) Yes (IVR prompts EN/UR/SD; STT any) Yes (form locale + dictated text)
Confirmation channel Email (reply) SMS WhatsApp template SMS (or voice callback) Channel chosen by visitor
MoSCoW (V1) [M] Must [M] Must [M] Must [S] Should [M] Must
Dependency Mailjet inbound route, sender-domain verification Short-code allocation, sender-ID approval WABA approval + template approvals Toll-free number, STT provider, IVR platform PWA offline storage + sync worker
Status contracted contracted planned (WABA pending) planned (Phase 4, /specs/en/08-integrations-spec/ §12.4) planned (PWA, Phase 1+)
Realizes FR-MCI-001 FR-MCI-002 FR-MCI-002 FR-MCI-003 FR-MCI-004

The web form (channel_of_origin = web) and Public API (channel_of_origin = api) are the baseline channels documented in /specs/en/06-ticket-workflow/ and /specs/en/12-api-contract/; they share the same pipeline once past ingest and are not re-specified here.


5. Per-channel Specification

Each channel is described against the integration template from /specs/en/08-integrations-spec/ §3 so contracts are comparable. Where the underlying transport is already specified in /specs/en/08-integrations-spec/ §5 (Mailjet, SMS, WhatsApp), this section covers only the intake-specific behaviour layered on top.

5.1 Email-to-ticket

Field Value
Purpose Receive complaints, follow-ups, and evidence by email to the facilitation desk address; turn each inbound into a new ticket or an appended reply; preserve attachments.
Direction Inbound (Mailjet inbound parse → SITP) with two-way replies (officer replies by email append to the same ticket thread).
Auth method Mailjet verifies DKIM/SPF on the inbound; SITP verifies the Mailjet webhook signature (08-integrations-spec §5.1).
Inbound address facilitation@sindhitportal.maahir.io (general intake) and per-ticket ticket-<trackingId>@inbound.sindhitportal.maahir.io (reply routing, see §7).
Key operations onInboundEmail (create or append), thread-match on subject tracking ID or In-Reply-To/References, AV-scan attachments.
Threading (1) If In-Reply-To/References header resolves to a tracked outbound message → append. (2) Else if subject contains a tracking ID regex SITP-\d{4}-[A-Z]{3,5}-\d{6} → append. (3) Else → dedup check → create or append per §3.3.
Subject → title First non-empty, non-"Re:"/"Fwd:" line of the subject; AI may rewrite for clarity into title while preserving subject_raw.
Body → description Stripped of quoted replies and signature blocks; original HTML/plain preserved as body_raw.
Attachments Extracted, AV-scanned, stored encrypted (FR-FILE-003, FR-FILE-004); rejected types logged.
Confirmation Reply on the originating thread with the tracking ID and portal URL, using the per-ticket inbound address as Reply-To so the next reply continues the thread.
Anti-abuse Per-sender rate limit (§10); DKIM/SPF fail → hold for review (FR-MCI-007).
Status contracted.
Realizes FR-MCI-001.

5.2 SMS-to-ticket

Field Value
Purpose Provide a low-bandwidth intake path for senders without reliable data — a short message to a short-code creates a ticket or appends to one; replies to ticket notifications append by default.
Direction Inbound (mobile-originated, MO) to short-code; two-way (replies to outbound SMS append).
Auth method MSISDN is the identity signal (SIM-bound); aggregator signs delivery receipts; SITP rate-limits per MSISDN.
Short-code & keyword A dedicated short-code (e.g., 82547) accepts: SITP <free-text complaint> to file; SITP STATUS <trackingId> to query status; a bare reply to any outbound ticket SMS appends.
Low-bandwidth contract Because an SMS body is rarely enough evidence, every created ticket carries a one-time short link (e.g., https://sindhitportal.maahir.io/t/<token>) to a mobile web form where the sender can add details, attachments, and CNIC; the link expires after a configurable window and is single-use.
Encoding UR/SD send as UCS-2 (70 chars/segment); EN as GSM-7 (160 chars/segment); concatenated segments are reassembled before parsing.
Threading If MO is a reply to a tracked outbound SMS (matched by aggregator message ID) → append. Else → dedup → create or append.
Confirmation Reply SMS carrying the tracking ID + short link; segment count is logged for cost analytics.
Anti-abuse Per-MSISDN daily/hourly caps (§10); keyword-less or nonsense bodies held for review.
Status contracted (SMS gateway stack-locked; short-code allocation is a procurement dependency).
Realizes FR-MCI-002.

5.3 WhatsApp-to-ticket

Field Value
Purpose Receive complaints, evidence, and voice notes on the channel IT companies already use daily; preserve media; offer two-way conversation that appends to the ticket thread.
Direction Inbound (Cloud API webhook) + two-way replies; outbound via pre-approved templates outside the 24-hour customer-service window.
Auth method X-Hub-Signature-256 (HMAC-SHA256) verified (08-integrations-spec §5.3); sender phone is WA-verified and must have opted in to receive template messages.
Key operations onMessage (text/image/document/voice/video), onMessageStatus, opt-in/opt-out.
Media handling Inbound media is downloaded from the Cloud API, AV-scanned, stored encrypted; image/PDF are OCR'd (FR-AI-001); voice notes are transcribed by STT (§5.4 engine reused) and the transcript stored as the description with the audio attached.
Threading replyContext (the tracking ID parsed from the most-recent outbound template) → append. Else → dedup → create or append.
Opt-in A sender must have explicitly opted in before any outbound template; an inbound message from a non-opted-in sender creates a ticket and single acknowledgement but does not auto-subscribe them to ongoing template sends.
Confirmation Pre-approved template (ticket_created_<locale> / ticket_updated_<locale>) carrying tracking ID + portal URL.
Anti-abuse Per-phone rate limits (§10); Meta's own anti-spam signals respected; opted-out senders cannot trigger outbound.
Status planned — WABA approval + template approvals pending (08-integrations-spec §12.2).
Realizes FR-MCI-002.

5.4 Toll-free IVR / Voice

Field Value
Purpose Provide a toll-free, no-smartphone intake path; capture complaints as voice recordings, transcribe them, create tickets; let callers look up status of an existing ticket by ID; queue callbacks when an agent is unavailable.
Direction Inbound (PSTN → IVR → SITP callback) + outbound (SMS confirmation, scheduled voice callback).
Auth method Caller ID (MSISDN) is the identity signal; CNIC can be captured via DTMF for higher-trust actions.
Number A single toll-free number (e.g., 0800-SITP) published on the public site and in all channel footers.
IVR call flow (1) Greeting in caller's chosen language (EN/UR/SD selectable by DTMF). (2) Menu: 1 Register a complaint · 2 Status of an existing ticket · 3 Speak to a facilitator · 0 Repeat. (3a) For complaints: prompt → record (configurable max duration, e.g., 3 minutes) → confirm and end. (3b) For status: caller enters tracking ID via DTMF or speaks it → system reads current status + SLA state. (3c) For facilitator: enter callback queue if no agent available; offer a callback slot.
Speech-to-text Recording transcribed by the STT engine (pluggable, same provider family as the AI/OCR service); transcript becomes the ticket description; the audio file is attached as evidence. Manual fallback: low-confidence transcripts route to a facilitator for verification before the ticket is created.
Language detection The caller-declared language drives prompts; STT supports EN/UR/SD with auto-fallback to the declared language on low confidence.
Confirmation SMS to caller with the new tracking ID and a short link to add attachments/CNIC (mirrors §5.2).
Status lookup Reads only public status info (FR-TKT-006); no PII read over the phone beyond what the caller provided.
Callback queue A BullMQ queue of pending callbacks with caller MSISDN, declared language, and preferred slot; an available facilitator claims the next callback and the system places the outbound call.
Anti-abuse Per-MSISDN rate limits (§10); excessive short calls dropped; abusive callers flagged for review.
Status planned — Phase 4 per /specs/en/08-integrations-spec/ §12.4 (toll-free number, IVR platform, STT contract required).
Realizes FR-MCI-003.

5.5 Walk-in / Offline Entry

Field Value
Purpose Let a front-desk / field officer enter a complaint on behalf of a walk-in visitor or a citizen met in the field, capturing the visitor's identity, the complaint, and any evidence; record the officer as the attribution actor; sync when connectivity returns.
Direction Inbound via PWA (online) or PWA offline queue (sync batch).
Auth method Officer is authenticated to the PWA via OIDC (08-integrations-spec §7) with the Filer-equivalent intake permission for S&ITD/partner facilitation counters; step-up auth (FR-ORG-009) required for sensitive tickets.
Capture fields Visitor identity (name, CNIC optional, phone, email optional), on-behalf-of organization (lookup by name/CNIC/SECP number, or individual for non-company walk-ins), title, description, category (officer-assisted), attachments (camera/documents).
Attribution Every walk-in ticket carries channel_of_origin = walkin, entered_by = <officerId>, and `on_behalf_of = <visitorId
Offline behaviour PWA stores the entry in IndexedDB with the original entry timestamp; the device signs the payload with the officer's session; on reconnect, a sync batch is posted and each entry ingests through the standard pipeline (dedup still applies across the batch and vs existing tickets). Conflicts (same visitor filed online in the meantime) are surfaced to the officer.
Confirmation Channel chosen by the visitor at the counter — SMS, WhatsApp, email, or printed receipt carrying the tracking ID.
Anti-abuse Per-officer rate limits (§10) to prevent bulk walk-in flooding; officers with abnormal volumes flagged for supervisor review.
Status planned (PWA first → React Native later per _context.md §3).
Realizes FR-MCI-004.

6. Inbound Message → Ticket Field Mapping

Every inbound message, regardless of channel, is mapped to the same canonical ticket model (see /specs/en/05-data-model/). The table below is the authoritative field map. Where a channel has no native value for a field, the pipeline fills it from AI (§3.3 stage 5) or leaves it for triage.

Ticket field Email source SMS source WhatsApp source IVR / Voice source Walk-in source
channel_of_origin email sms whatsapp ivr walkin
title Subject (cleaned) First ~80 chars of body, AI-rewritten First line / AI summary AI summary of transcript Officer-entered
description Body (stripped) Full MO body + short-link note Text + transcript of any voice note STT transcript (with audio attached) Officer-entered
requested_by (rep) Match email → rep/org, else Provisional (§7) Match MSISDN → rep/org/CNIC, else Provisional Match phone → rep/org, else Provisional Match MSISDN → rep/org, else Provisional Officer-selected org/rep or Provisional visitor
entered_by (actor) Officer ID
on_behalf_of Visitor identity
category AI suggestion → triage confirm AI suggestion → triage confirm AI suggestion → triage confirm AI suggestion → triage confirm Officer-entered + AI suggestion
target_dept / section AI routing suggestion (FR-AI-003) same same same same
priority / urgency AI urgency (FR-AI-004) same same same same
locale Body script + Accept-Language UCS-2 detection → ur/sd/en Text/voice language detection Caller-declared + STT PWA form locale
attachments[] Email attachments (AV-scanned) WA media (AV-scanned) Voice recording (audio) Officer-uploaded (AV-scanned)
subject_raw / body_raw Original headers + raw body Raw MO PDU Raw webhook payload Raw call metadata Raw form payload
inbound_ref Mailjet message ID Aggregator MO ID WA message ID IVR call ID PWA sync batch + entry UUID
thread_target Resolved by In-Reply-To/References or tracking ID Resolved by reply-context Resolved by replyContext (n/a — always new) (n/a — always new)
redaction_map_ref On-prem redaction map handle same same same same
sla_tier From dept/category/urgency config (FR-TKT-007) same same same same
created_at Receive timestamp MO timestamp Webhook timestamp Recording-end timestamp Original entry timestamp (preserved across offline sync)

Tickets created from inbound channels are otherwise indistinguishable from web/API tickets in the data model; downstream SLA, escalation, proof-of-resolution, and analytics logic does not branch on channel_of_origin.


7. Identity Linking

Inbound messages rarely carry a logged-in session; they carry a channel identity (email address, MSISDN, WA-verified phone, caller ID, CNIC). The pipeline resolves that identity to an existing organization / representative, or creates a Provisional identity, before the ticket is created (MC-2).

7.1 Resolution order

The identity-linking service, invoked at the normalize stage, attempts resolution in this priority order:

  1. Exact rep match — the inbound identifier matches a representative's registered email or phone (org_rep table).
  2. Organization domain match — for email, the sender domain matches a registered organization's verified domain (file-first domain-email signal, _context.md §5).
  3. CNIC match — the inbound discloses or carries a CNIC (IVR DTMF, walk-in officer entry, message body extracted via PII detect) that matches a representative's CNIC.
  4. Provisional identity — no match found; a Provisional sender record is created with the channel identifier, marked unverified, and the ticket is attributed to it. The sender is invited to register; on successful registration, the provisional identity is merged into the new rep and the ticket is re-attributed.

7.2 Identity table

Match outcome Ticket attribution Subsequent behaviour
Exact rep requested_by = rep; org inherited Full RBAC applies; rep sees ticket in their portal.
Domain match requested_by = Provisional (domain); org linked Invitation to register; on register, rep inherits.
CNIC match requested_by = rep (CNIC-confirmed) Step-up trust; eligible for sensitive actions.
No match (Provisional) requested_by = Provisional; org = individual or named Ticket proceeds; registration invitation sent; merges on register.

Identity linking is read/write against the identity tables only at the normalize stage; later stages read the resolved identity from the pipeline context. This keeps PII handling in one audited place.


8. Channel-agnostic Routing

Once a ticket is created, routing is identical regardless of channel (MC-4). The AI routing suggestion (FR-AI-003) and the department/category configuration drive the target department and section, exactly as for web tickets (see /specs/en/06-ticket-workflow/ §4–§5). The only channel-aware behaviour is fallback confirmation (§12).

Two guarantees apply:


9. Multilingual Inbound

The Portal serves EN/UR/SD as locked languages (_context.md §2). Inbound messages arrive in any of these (and occasionally in mixed or transliterated forms). The pipeline handles this in three steps:

Step Behaviour
Detect Language detection runs at AI-categorize stage; the script (Latin / Nastaliq / Naskh) and a confidence score are attached. Mixed-language messages are tagged with the dominant language and flagged.
Store The original message is preserved unchanged (locale_detected); a translation is not stored as the description — it is presented alongside for the officer.
Translate For the assigned officer, an on-demand AI translation (FR-AI-006) is shown beside the original, clearly marked machine-generated, in the officer's working language. The officer can request alternate phrasing; the original is never overwritten.

IVR adds a caller-declared language at the start of the call; the IVR prompts and STT engine use it as the primary language with auto-fallback. Walk-in officers select the form locale at the counter; if the visitor dictates in another language, the officer's recorded text is language-detected at sync.


10. Rate-limiting & Anti-abuse

Inbound intake is an open surface; it must resist spam, flooding, and abuse without blocking legitimate senders (FR-MCI-007, _context.md §6). Controls are layered:

Layer Scope Default (configurable) Action on breach
Per-source rate limit Per email / MSISDN / WA phone ≤ 10 inbound/hour, ≤ 50/day Throttle (drop excess, send 1 "rate-limited" notice/day)
Per-IP rate limit Web/API inbound 600 req/min read, 60/min create (per /specs/en/08-integrations-spec/ §8.1) 429 with Retry-After
Per-officer rate limit Walk-in officer ≤ 100 entries/day Soft-cap; supervisor notified
DKIM/SPF / WA signature Email inbound Required Fail → hold for facilitator review (FR-MCI-007)
Keyword / content filter SMS, WA Keyword required for filing (e.g., SITP) Non-keyword messages held for review
Flood detection Per-source burst > threshold in 60 s Temporary block (5–60 min) + review queue
Spam classification All channels AI spam score High-score inbound quarantined; facilitator reviews; legitimate releases create the ticket with original timestamp
Sender reputation Persistent per-identifier Decays on good behaviour Low-reputation senders' inbound always held for review
CAPTCHA / step-up Public status lookup (FR-TKT-006) After N invalid attempts CAPTCHA-gated; further abuse → IP rate-limit

Every rate-limit and anti-abuse decision is audit-logged with the rule that fired. Held/quarantined inbound appears in a review queue for the on-shift facilitator; releasing an item runs it through the rest of the pipeline from stage 3 onward.


11. Dedup & Merge across Channels

A complainant may file the same issue by email in the morning and by WhatsApp that afternoon; a colleague at the same company may SMS it an hour later. The system must detect this and suggest a merge, never auto-merge silently (FR-MCI-005, FR-TKT-015).

11.1 Detection signals

Signal Weight Notes
Same resolved identity (rep/org) High After §7 resolution.
Same MSISDN/email/CNIC (cross-channel) High Phone from WhatsApp + email from email = same person if CNIC matches.
Same organization + similar category Medium Same company, same problem family.
Textual similarity of title/description (FR-AI-007) Medium Threshold-tuned; respects Confidential/VIP scope.
Proximity in time Low (tie-breaker) Same day vs same year.
Same attachment hash Medium Identical evidence file across channels.

11.2 Merge decision

The dedup check (pipeline stage 4) returns candidate matches with similarity scores. The triage officer decides:

11.3 Dedup & merge flow

The diagram shows the dedup decision as a state machine driven by the triage officer.

stateDiagram-v2 [*] --> Detected: dedup check finds candidates Detected --> Surfaced: candidates + scores on triage view Surfaced --> ProceedNew: officer: "new" Surfaced --> Linked: officer: "link" Surfaced --> Merged: officer: "merge" ProceedNew --> [*]: audit-logged, ticket routes normally Linked --> [*]: FR-TKT-017 relation created Merged --> Consolidating: choose primary Consolidating --> Locked: secondary locked Consolidating --> Consolidated: attachments/comments/watchers moved Locked --> Consolidated Consolidated --> Notified: filer told primary ID Notified --> [*]

12. Fallback Chain & Cross-channel Resilience

Each channel depends on a third-party provider (Mailjet, the SMS aggregator, Meta, the IVR/PSTN carrier, and — for walk-in — the officer's connectivity). No single provider is allowed to be a hard dependency for inbound intake, and confirmation delivery must always find a working path.

Concern Resilience mechanism
Inbound ingestion Each channel has its own receiver and queue; one provider being down does not block the other four. The intake pipeline is provider-agnostic past stage 1.
Confirmation delivery A fallback chain per sender tries the originating channel first, then the sender's other known channels (in preference order: WhatsApp → SMS → email → in-app), stopping at the first successful ack. Preference is configurable per sender and per message class.
Provider circuit breaker Per-adapter breaker (08-integrations-spec §2.1); when open, that channel's outbound is short-circuited to the fallback chain and ops is alerted.
DLQ & replay Confirmations that exhaust retries land in the per-adapter DLQ; a worker replays them once the provider recovers, with the original timestamp preserved.
Cross-provider SMS Jazz and Telenor are both contracted for failover (08-integrations-spec §5.2); a failing primary aggregator fails over per message class.
Public status The public status page (and the IVR status-lookup option §5.4) reads from a cached aggregate, so a provider outage on one channel does not blind the public.

A channel being read-only down (inbound fine, outbound broken) is surfaced to the triage officer on the ticket so manual follow-up can happen via another channel.


13. Offline / Field Intake

Walk-in/offline entry (§5.5) is the most operationally demanding channel because the officer's device may be offline at the point of capture. The contract:

Concern Behaviour
Capture PWA form works fully offline; entries are stored in IndexedDB with the original entry timestamp and the officer's signed session.
Storage limits Local cap (e.g., 200 pending entries) with a warning near the cap; oldest-unsynced surfaced to the officer.
Conflict If the same visitor/provisional identity filed online while the officer was offline, dedup (§11) catches it at sync and surfaces the conflict to the officer for resolution.
Sync A background sync worker posts batches when connectivity returns; each entry ingests through the standard pipeline; the original entry timestamp is preserved on the created ticket (FR-MCI-004), not the sync timestamp.
Partial sync Sync is per-entry idempotent (idempotency key = PDA entry UUID); a network drop mid-batch resumes from the next entry.
Audit Each offline entry records device ID, officer ID, capture timestamp, sync timestamp, and any conflict resolution chosen.
Media Photos/attachments are captured and stored locally, then uploaded at sync and AV-scanned as part of the pipeline.

14. Channel Analytics

Channel of origin is a first-class analytics dimension (FR-MCI-006). The Analytics module (/specs/en/17-analytics-kpis/) produces the following channel-scoped metrics, sourced from the anl_mv_* materialized views and respecting RBAC + the public suppression rules (AP-3, AP-4).

Metric Grain Audience Notes
Volume by channel day × dept × channel Staff / DG / Secretary / SACM / Public (anonymized) Stacked bar; drill-down to tickets (AP-2).
Channel mix over time week × channel Leadership Trend; flags channel-shift (e.g., WhatsApp overtaking email).
First-response time by channel day × channel Staff / DG Tests MC-4 (channel-agnostic FRT).
Resolution rate by channel month × channel Leadership / Public Should be ~equal if MC-4 holds; divergence is an investigation signal.
CSAT by channel month × channel Staff / DG Lower CSAT on a channel may indicate bandwidth friction (e.g., SMS).
Conversion: Provisional → registered week × channel S&ITD product How often a walk-in/IVR/SMS filer goes on to register.
Inbound confirmation success rate day × channel Ops Drives §12 fallback chain tuning.
Spam/abuse held day × channel Ops / Trust & Safety Volume in the review queue (§10).
Dedup / merge rate week × channel S&ITD product How many cross-channel duplicates are caught.
Offline-sync lag day × officer Ops Max time between capture and sync for walk-in (§13).

All channel metrics share the same definitions and rounding rules as their channel-agnostic counterparts (AP-5); the only added dimension is channel_of_origin.


15. Data Model Touchpoints

This document does not redefine the data model (see /specs/en/05-data-model/) but records the multi-channel-specific fields and tables it relies on:

Table / field Purpose Originating FR
tickets.channel_of_origin Enum: web, api, email, sms, whatsapp, ivr, walkin. FR-MCI-006
tickets.entered_by Officer ID for walk-in; null otherwise. FR-MCI-004
tickets.on_behalf_of Visitor/provisional identity for walk-in. FR-MCI-004
tickets.locale_detected Detected language of the inbound. §9
tickets.inbound_ref Channel-specific message ID (Mailjet/WA/MO/IVR call/PDA UUID). §6
tickets.subject_raw / body_raw Original inbound payload, preserved. FR-MCI-001
tickets.redaction_map_ref On-prem handle to restore PII. FR-AI-009
int_call (direction = inbound) Audit of every inbound payload. 08-integrations-spec §2.1
org_rep_channel Channel identifiers per rep (email, MSISDN, WA phone). §7
int_ratelimit Per-source/per-officer rate-limit state. FR-MCI-007
ticket_relation Cross-ticket link/merge relations (typed). FR-TKT-015, FR-TKT-017
anl_mv_channel_* Channel-scoped aggregates for analytics. §14

16. End-to-end Sequence (one inbound → one ticket)

The sequence below shows a single WhatsApp inbound becoming a new ticket, with confirmation. The same shape applies to every channel; only the receiver and confirmation adapter change.

sequenceDiagram autonumber participant Sender as Sender (WhatsApp) participant WA as WhatsApp Cloud API participant RCV as Inbound Webhook Receiver participant Q as Intake Queue (BullMQ) participant ID as Identity Linking participant DEDUP as Dedup Service participant AI as AI (PII · categorize · lang) participant TKT as Ticketing Core participant NOT as Notifications (fallback) Sender->>WA: sends complaint text + photo WA->>RCV: webhook (signed) RCV->>RCV: verify X-Hub-Signature-256, replay check, rate-limit gate RCV->>Q: enqueue InboundMessage Q->>AI: PII detect + redact (photo OCR'd) Q->>ID: resolve phone → rep/org or Provisional ID-->>Q: resolved identity Q->>DEDUP: similarity vs open + recent tickets DEDUP-->>Q: no confident match Q->>AI: categorize + routing + urgency + language AI-->>Q: suggestions + locale Q->>TKT: create ticket (channel_of_origin=whatsapp, locale, attachments) TKT-->>Q: tracking ID SITP-2026-ITD-000045 Q->>NOT: confirm on WhatsApp (fallback: SMS → email) NOT->>WA: send ticket_created_sd template WA->>Sender: "Your ticket SITP-2026-ITD-000045 is registered"

The receiver verifies the signature and applies the rate-limit gate before any pipeline work (steps 2–3). PII redaction runs before any cloud AI call (step 5). Identity and dedup run in parallel with categorization where data dependencies allow. Confirmation is the last step, with cross-channel fallback (§12) so a WhatsApp outage at confirmation time does not strand the sender.


17. Non-functional Requirements (MCI-specific)

These NFRs refine the system-wide NFRs (03-non-functional-requirements/en.md) and the integration NFRs (/specs/en/08-integrations-spec/ §11) for the multi-channel intake surface.

ID Concern Target
NFR-MCI-001 Inbound ingestion latency Ack to provider ≤ 2 s p95 (so the provider's webhook timeout is not hit); pipeline work runs async.
NFR-MCI-002 End-to-end create latency (receive → ticket created) ≤ 30 s p95 for non-AV-heavy inbound; ≤ 60 s p95 when attachments require AV + OCR.
NFR-MCI-003 Confirmation delivery ≤ 60 s p95 from ticket creation; fallback chain kicks in after provider timeout.
NFR-MCI-004 Availability Intake plane ≥ 99.9%; no single provider outage degrades intake of the other channels.
NFR-MCI-005 Idempotency Every inbound keyed by provider message ID; replays create/update exactly once.
NFR-MCI-006 Offline capture resilience Walk-in entries survive device reboot, app kill, and > 24 h offline; no entry lost on sync failure.
NFR-MCI-007 Privacy Inbound payloads are PII-redacted before any cloud AI call; raw payloads stored encrypted; access logged.
NFR-MCI-008 Auditability Every inbound recorded in int_call; every create/append decision (incl. dedup choice) in audit_events.
NFR-MCI-009 Observability Per-channel dashboards (volume, latency, errors, DLQ depth, spam-held); alerts on anomalies.
NFR-MCI-010 Fairness Channel of origin must not change SLA, routing priority, or RBAC; verified by automated test per release.

18. FR Traceability

This document is the detailed expansion of the MCI module FRs in /specs/en/02-functional-reqs/ §MCI.

FR Title MoSCoW Realized in
FR-MCI-001 Create tickets from inbound email [M] §5.1, §6, §7
FR-MCI-002 Create and update tickets from WhatsApp and SMS [M] §5.2, §5.3, §6, §7
FR-MCI-003 Provide toll-free IVR/voice intake [S] §5.4, §6
FR-MCI-004 Provide walk-in/offline intake by staff [M] §5.5, §13, §6
FR-MCI-005 Detect duplicates across channels at intake [M] §3.3 (stage 4), §11
FR-MCI-006 Capture channel of origin on every ticket [M] §4 (matrix), §6, §15
FR-MCI-007 Rate-limit and verify inbound senders [M] §3.3 (gate), §10

Cross-cutting FRs relied upon: FR-TKT-001 (tracking IDs), FR-TKT-006 (anonymous status lookup, used by IVR), FR-TKT-007/FR-TKT-008 (SLA + escalation, channel-agnostic), FR-TKT-015/FR-TKT-017 (merge + link), FR-FILE-003/FR-FILE-004 (AV + encrypted storage), FR-AI-002/FR-AI-003/FR-AI-004/FR-AI-006/FR-AI-007/FR-AI-009 (AI capabilities), FR-ORG-009 (step-up for sensitive walk-in), FR-NOT-001/FR-NOT-003 (notifications + inbound reply append).


19. Open Questions / TBD

# Item Status
1 Toll-free number allocation and IVR platform vendor (cloud IVR vs on-prem PBX). TBD (procurement).
2 STT provider for IVR/voice notes — cloud (Azure/Google/AWS) vs self-hosted (Whisper/Vosk) given UR/SD quality. TBD with AI pluggability policy.
3 Whether SMS intake requires a dedicated short-code or can reuse the existing sender-ID reverse path. TBD with aggregator.
4 Provisional-identity merge semantics when the same person later registers as a rep under a different org. TBD with ORG module.
5 Whether IVR callback queue integrates with the Internal Comms presence (Module F) for facilitator availability. TBD.
6 Public suppression thresholds for channel-mix analytics on the transparency dashboard. TBD with analytics policy.
7 Retention of raw inbound payloads (subject_raw, body_raw, audio) per data-classification policy. TBD with /specs/en/11-security-compliance/.

End of document.