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-001 … FR-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:
- Email-to-ticket
- SMS-to-ticket
- WhatsApp-to-ticket
- Toll-free IVR / voice
- 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:
- Ingest — receive the raw payload from the channel adapter (Mailjet inbound parse, SMS MO, WhatsApp Cloud API webhook, IVR recording callback, PWA sync batch).
- Normalize — extract a canonical
InboundMessage(sender identifiers, raw text, attachments, locale hints, channel-specific metadata) and drop or quarantine anything that fails structural validation. - 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. - 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. - AI categorize — classify and suggest routing (
FR-AI-002,FR-AI-003); detect urgency/sentiment (FR-AI-004); detect language (§9). - Create or append — either create a new ticket (
FR-TKT-001tracking ID assigned) or append as a comment to the matched existing ticket (thread matching,§7). Channel of origin is stamped (FR-MCI-006). - 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.
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:
- No confident dedup match (or only resolved matches) → create a new ticket with the canonical tracking ID (
FR-TKT-001), stampchannel_of_origin(FR-MCI-006), attach the AI suggestions for the triage officer, and trigger channel-agnostic routing (§8) to the target department/section and SLA tier. - Confident dedup match against an open ticket → append the inbound as a public comment on the matched ticket, re-using that ticket's existing routing; the dedup choice (proceed / link / merge) is surfaced to the triage officer and audit-logged per
FR-MCI-005. - Thread-matched reply (an inbound that explicitly references a tracking ID, e.g. a reply to a notification) → always appends, regardless of dedup score (
§7).
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 | SMS | 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:
- Exact rep match — the inbound identifier matches a representative's registered email or phone (
org_reptable). - Organization domain match — for email, the sender domain matches a registered organization's verified domain (file-first domain-email signal,
_context.md§5). - 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.
- Provisional identity — no match found; a
Provisionalsender record is created with the channel identifier, markedunverified, 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:
- A ticket created from a low-bandwidth channel (SMS, IVR) is not penalized in routing or SLA because it lacks a long description. The AI summary + officer triage enriches the description before routing is finalized; the FRT clock is not started until triage completes (consistent with the
New → Triaged → Assignedmodel in/specs/en/06-ticket-workflow/§2). - A ticket created from a Provisional sender (
§7) routes normally; verification status does not gate triage (file-first / verify-in-parallel,_context.md§5).
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:
- Proceed as new — no merge; the choice is audit-logged.
- Link — create a typed relation (
related to,duplicate of) perFR-TKT-017; both tickets remain independently tracked. - Merge — fold the newer ticket into the older (or officer-chosen primary) per
FR-TKT-015: secondary is locked, attachments/comments/watchers consolidate onto the primary, the filer is notified with the primary's tracking ID.
11.3 Dedup & merge flow
The diagram shows the dedup decision as a state machine driven by the triage officer.
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.
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.