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

Integrations Specification

Per-integration contracts and the platform integration layer (API, webhooks, SSO) that connect the Sindh IT Portal — Facilitation Desk (SITP) to government registries, communication providers, video platforms, and partner consumers.

Field Value
Doc ID 08
Status Draft
Owner S&ITD / MAAHIR
Languages EN (master) · UR · SD
Related docs /specs/en/15-tech-architecture/, /specs/en/12-api-contract/, /specs/en/11-security-compliance/, 03-non-functional-requirements/en.md, /specs/en/05-data-model/

1. Scope & How to Read This Document

This document is the authoritative contract for every external system SITP talks to and every external surface it exposes. It covers:

The technology stack and high-level architecture are locked in _context.md §3 and detailed in /specs/en/15-tech-architecture/ §13. This document expands §13 of the tech architecture into per-integration contracts. Where this document and the tech architecture disagree, the tech architecture is the source of truth for the platform and this document is the source of truth for individual integration contracts.


2. Integration Architecture

Every external system — government registry, communication provider, video platform, partner consumer — is reached only through an adapter inside the NestJS Integrations module. No module outside Integrations imports a vendor SDK, calls a vendor HTTP endpoint directly, or reads a vendor credential. This is the anti-corruption boundary of the system: it isolates SITP's domain language and data model from each external system's quirks, schema changes, and outages.

2.1 Architectural invariants

# Invariant Enforcement
1 Adapter pattern Every external system has exactly one adapter module implementing a stable internal interface. All call sites program to the interface, never the SDK.
2 Anti-corruption layer Adapters translate external payloads into SITP domain objects and back. External field names never leak past the adapter.
3 Secrets in the vault Credentials, API keys, signing secrets, and OAuth client secrets live in the secrets vault (see /specs/en/15-tech-architecture/ §16), fetched at startup, rotated per §11.7. No secret is ever committed to the repo or read from a plain env file.
4 Idempotency keys Every outbound state-changing call carries an idempotency key (Idempotency-Key header or vendor equivalent). The adapter and the remote system honor it so retries never double-write.
5 Retries with exponential backoff + jitter Transient failures retry with capped exponential backoff and random jitter; max attempts and the dead-letter queue catch permanently-failed calls.
6 Circuit breakers per adapter Each adapter has its own breaker (e.g., Opossum). A tripped breaker fails fast instead of piling up connections during an external outage.
7 Dead-letter queue (DLQ) Calls that exhaust retries land in a per-adapter DLQ in BullMQ/Redis; ops triages and replays from there.
8 Audit every call Every outbound call and every inbound webhook payload is recorded (sensitive fields redacted) in the int_call table with direction, adapter, target, latency, status, and a redacted payload excerpt.
9 Queued for slow/external work Any call estimated > 250 ms or that crosses the trust boundary is enqueued to BullMQ, never run inline in a request.
10 Feature-flagged Each adapter sits behind a feature flag so it can be disabled per environment/department without redeploy.

2.2 Integration layer diagram

The diagram below shows the runtime flow of a single outbound call from a domain module through the integration layer to an external system, and the corresponding inbound path for webhook deliveries from external systems back into SITP.

flowchart TB subgraph Domain["NestJS domain modules"] TKT["Tickets"] ORG["Org / RBAC"] NOT["Notifications"] MTG["Meetings / TRI / MoM"] REG["Registration"] end subgraph IntLayer["Integration layer (anti-corruption)"] IFACE["Stable adapter interfaces<br/>(RegistryAdapter · CommsAdapter · VideoAdapter · ... )"] ADP["Concrete adapters<br/>(NADRA · SECP · FBR · SRB · PSEB · e-Office · Mailjet · SMS · WA · Zoom/Meet/Teams)"] RES["Resilience wrapper<br/>(idempotency · retry+backoff · circuit breaker · DLQ)"] AUD["Audit recorder → int_call"] SEC["Secrets resolver (vault)"] MAP["Data mapping / master-data lookup"] end subgraph Q["BullMQ queues"] OQ["Outbound queue (per adapter)"] DLQ["Dead-letter queue (per adapter)"] IQ["Inbound webhook queue"] end subgraph Ext["External systems"] NADRA["NADRA Verisys"] GOV["SECP · FBR · SRB · PSEB · NITB e-Office"] MJ["Mailjet"] SMS["SMS gateway"] WA["WhatsApp Cloud API"] VID["Zoom / Meet / Teams"] end subgraph Inbound["Inbound path"] HOOK["Webhook receiver<br/>(signature verify · replay protect)"] end TKT --> IFACE ORG --> IFACE NOT --> IFACE MTG --> IFACE REG --> IFACE IFACE --> ADP SEC --> ADP MAP --> ADP ADP --> RES RES --> AUD RES --> OQ OQ --> NADRA OQ --> GOV OQ --> MJ OQ --> SMS OQ --> WA OQ --> VID RES -- "exhausted" --> DLQ MJ -. "delivery/bounce webhook" .-> HOOK WA -. "inbound reply webhook" .-> HOOK GOV -. "e-Office status callback" .-> HOOK HOOK --> IQ IQ --> Domain

The integration layer sits between the domain modules (which never know which vendor is plugged in) and the external systems. A domain module calls a stable interface such as RegistryAdapter.verifyCnic(...); the Integrations module resolves the concrete adapter (NADRA), pulls credentials from the vault, applies master-data mapping, wraps the call in the resilience layer (idempotency key → retry → circuit breaker → DLQ), records an audit row, and dispatches the actual HTTP/SDK call from a BullMQ worker. The same physical layer also accepts inbound webhooks: the webhook receiver verifies the signature, rejects replays, enqueues a normalized event, and a worker dispatches it back into the appropriate domain module. The DLQ captures any call that cannot complete within the retry budget so ops can triage and replay.

2.3 Adapter interface (illustrative)

Every adapter family implements a small, stable interface. New vendors are added by writing a new concrete adapter behind an existing interface — the domain modules are not touched.

// Illustrative TypeScript contract — the shape every adapter obeys.
interface RegistryAdapter {
  verifyCnic(req: CnicVerifyRequest): Promise<CnicVerifyResponse>;
  lookupCompany(req: CompanyLookupRequest): Promise<CompanyLookupResponse>;
}
interface CommsAdapter {
  sendEmail(req: EmailRequest): Promise<MessageSendResult>;
  sendSms(req: SmsRequest): Promise<MessageSendResult>;
  sendWhatsApp(req: WhatsAppRequest): Promise<MessageSendResult>;
}
interface VideoAdapter {
  createMeeting(req: MeetingCreateRequest): Promise<MeetingCreateResult>;
  getRecording(meetingId: string): Promise<RecordingResult>;
}

3. Integration Template

Every integration in §4–§7 is described using the same template so contracts are comparable and reviewable. The template fields:

Field Meaning
Purpose What SITP uses this integration for.
Direction Inbound (external → SITP), Outbound (SITP → external), or Bidirectional.
Auth method How SITP authenticates to the external system, and (for inbound) how the external system authenticates to SITP.
Key operations The discrete calls/messages the integration supports.
Data contract Request and response field lists (and sample JSON where helpful).
Error handling & SLA Status codes handled, retry policy, circuit-breaker threshold, target latency/availability.
Assumptions / dependencies MoUs, registrations, sender-ID approvals, template approvals, network access (IP whitelisting), etc.
Status planned (no contract yet) or contracted (MoU/access in place).

4. Government Identity & Registry Integrations

These integrations back the file-first, verify-in-parallel registration model (see _context.md §5): a company rep registers, gets a Provisional account, files immediately, and SITP verifies identity and entity standing in the background across NADRA, SECP, FBR, SRB, and PSEB. The same adapters serve ad-hoc verification during ticket handling.

Cross-cutting dependency. Every adapter in this section requires an MoU and/or API registration with the respective authority before it can go live. These are governance dependencies, tracked in §12, not engineering tasks. Until an MoU is signed, the adapter is shipped dark behind a feature flag and lookups return "verification pending — manual review" so the registration flow is unblocked.

4.1 NADRA Verisys — CNIC & identity verification

Field Value
Purpose Verify a representative's identity from their CNIC; confirm name, date of birth, family tree, and CNIC status (active/cancelled). Optional biometric verification for high-trust actions (Primary Authorized Rep transfer, sensitive-ticket closure).
Direction Outbound.
Auth method IP whitelisting at the NADRA endpoint + username/password (or mutual TLS / API key, per the executed MoU). Credentials in the secrets vault. Source IP is SITP's egress IP, registered with NADRA.
Key operations verifyCnic(cnic) → identity record; verifyBiometric(cnic, biometricToken) → match score (optional, gated by feature flag).
Data contract — request cnic (13 digits, validated), purpose ("registration" | "rep-transfer" | "sensitive-action"), optional biometricToken.
Data contract — response cnic, nameEn, nameUr (if available), fatherOrHusbandName, dob, gender, familyTreeId, presentAddress, status (active | cancelled | not-found), verificationRef (NADRA transaction id), verifiedAt.
Error handling & SLA 200 → success; 404 → CNIC not found (returned as not-found, not an error); 401/403 → credential/IP failure (alert ops, no retry); 408/5xx → retry per §11.2 then DLQ. Target p95 ≤ 8 s; breaker opens after 5 consecutive failures.
Assumptions / dependencies MoU with NADRA for Verisys API access. IP whitelisting requires a static egress IP. Sovereign data: this adapter is allowed only via on-prem or NADRA-approved channel; payloads are Restricted data class, stored in int_call with heavy redaction (only verificationRef + status retained, not the full identity record).
Status planned — MoU required.

4.2 SECP — company lookup

Field Value
Purpose Validate a registered company by its incorporation/registration number; fetch legal name, status (active/dormant/struck-off), registered office, and directors. Drives the Verified badge during registration and the directors list for rep authorization.
Direction Outbound.
Auth method API key + (likely) IP whitelisting, per the SECP data-access MoU.
Key operations lookupCompany(incorporationNo), lookupCompanyByName(name) (fuzzy, for search-assist during filing).
Data contract — request incorporationNo (e.g., 0012345) or name; jurisdiction (federal).
Data contract — response incorporationNo, name, status, registrationDate, registeredOfficeAddress, businessActivity, directors[] (name, cnic (if disclosed), designation), lookupRef.
Error handling & SLA As §4.1; target p95 ≤ 6 s. Mismatches between the user-entered name and the SECP record flag the registration for manual review rather than auto-reject.
Assumptions / dependencies MoU / API registration with SECP. SECP also exposes a public-name-search website that can be used as a fallback for lookups while the API MoU is in progress (manual verification).
Status planned — MoU required.

4.3 FBR — NTN & active-tax-filer status

Field Value
Purpose Verify a company's National Tax Number (NTN), active-filer status, and tax profile. Contributes to the Verified badge and is a signal in the company trust score.
Direction Outbound.
Auth method API key + IP whitelisting, per FBR access agreement. FBR also publishes a public active-taxpayer list (ATL) refreshed periodically, used as a read-only fallback.
Key operations lookupNtin(ntn), filerStatus(ntnOrCnic).
Data contract — request ntn (7–8 digits) and/or cnic.
Data contract — response ntin, name, status (active | inactive), filerStatus (active-filer | non-filer), taxOffice, businessActivity, asOfTaxYear, lookupRef.
Error handling & SLA As §4.1; target p95 ≤ 6 s. Filer status is point-in-time per tax year — the response records the asOfTaxYear so stale data is detectable.
Assumptions / dependencies MoU / API access with FBR. Filer status changes yearly; ATL refresh lag is acceptable.
Status planned — MoU required.

4.4 SRB — Sindh sales tax registration (STRN)

Field Value
Purpose Validate a company's Sales Tax Registration Number (STRN) issued by the Sindh Revenue Board — particularly relevant for IT/services companies operating in Sindh.
Direction Outbound.
Auth method API key + IP whitelisting, per SRB access agreement.
Key operations validateStrn(strn).
Data contract — request strn (format-validated).
Data contract — response strn, legalName, status (active | suspended | cancelled), registrationDate, taxAuthority (SRB), lookupRef.
Error handling & SLA As §4.1; target p95 ≤ 5 s.
Assumptions / dependencies MoU / API access with SRB. SRB is a Sindh-provincial body, so this is the highest-priority provincial integration.
Status planned — MoU required (provincial priority).

4.5 PSEB — IT-industry membership validation

Field Value
Purpose Validate membership with the Pakistan Software Export Board — both company membership and freelancer/individual membership. PSEB members receive a trust signal in the Verified badge and may be eligible for priority routing on IT-sector tickets.
Direction Outbound.
Auth method API key, per PSEB data-sharing agreement.
Key operations validateMembership({ memberType, membershipNo }) for memberType ∈ { company, freelancer }.
Data contract — request memberType, membershipNo (or cnic for freelancers), companyName.
Data contract — response membershipNo, memberType, name, status (active | expired | not-found), validUntil, lookupRef.
Error handling & SLA As §4.1; target p95 ≤ 5 s. An expired membership does not block registration — it only removes the trust signal.
Assumptions / dependencies Data-sharing agreement with PSEB. PSEB membership data lags renewals by days; an "expired" result that is < 30 days past validUntil is treated as "pending renewal".
Status planned — agreement required.

4.6 NITB e-Office — official file movement

Field Value
Purpose Push a SITP ticket, MoM, or resolution certificate into the government's e-Office system as an official file movement so the receiving department can process it through their statutory file workflow; pull status back so SITP reflects the official-file state on the ticket.
Direction Bidirectional. Outbound (create file movement, attach document); Inbound (status callbacks from e-Office).
Auth method Service account in e-Office (managed by NITB) + API key; inbound callbacks signed with a shared HMAC secret.
Key operations createFileMovement(ticketOrMom), attachDocument(fileId, docRef), getStatus(fileId) (poll fallback), inbound onStatusChange(fileId, status).
Data contract — push request sourceRef (SITP-2026-ITD-000045), type (ticket | mom | resolution), title, summary, originatingDept (S&ITD), targetDept, priority, documents[] (fil_attachment refs → presigned URLs the e-Office side fetches), requestedBy.
Data contract — push response fileId (e-Office's file number), status (submitted), acceptedAt.
Data contract — status callback fileId, sourceRef, status (submitted | under-process | approved | returned | rejected), currentDesk, updatedAt, note.
Error handling & SLA 2xx → success; 409 → file already exists (treat as idempotent success using the idempotency key); 5xx → retry per §11.2 then DLQ; missing callback → poll getStatus every 30 min as fallback. Target p95 ≤ 10 s for push; status freshness ≤ 30 min.
Assumptions / dependencies MoU with NITB and a service account in the e-Office instance. Document exchange requires e-Office to fetch from presigned URLs (or alternatively receive base64 payloads — adapter supports both).
Status planned — MoU required. High value for inter-departmental legitimacy.

5. Communications Integrations

The communications adapters back the notifications pipeline (see /specs/en/15-tech-architecture/ §12). They are outbound by default with inbound paths for two-way replies (email reply, WhatsApp reply) that append to a ticket thread. Every outbound send carries an Idempotency-Key so duplicate queue jobs never send twice.

5.1 Mailjet — transactional email

Field Value
Purpose Transactional notifications (ticket created/updated/resolved, MoM published, escalation, digests), multilingual templated email, and inbound reply parsing (a reply to a notification email is appended to the originating ticket).
Direction Bidirectional (outbound send; inbound delivery/bounce/spam webhooks + inbound parse of replies).
Auth method SMTP (for legacy send paths) and REST API with API key + secret in the vault. Sender domain authentication: SPF, DKIM, and DMARC configured for the sender domains maahir.io and sindhitportal.maahir.io; Mailjet's verified sender domain record in place. Inbound webhook signed with Mailjet's signature header, verified by SITP.
Key operations sendEmail(...), sendTemplate(...), inbound onDelivery, onBounce, onSpam, onInboundReply.
Data contract — send request to[], cc[], bcc[], from (no-reply@sindhitportal.maahir.io), replyTo (per-ticket address that routes inbound back to SITP), templateId (Mailjet template ID), variables (locale, ticketId, names, dates — both Gregorian and Hijri pre-rendered server-side), idempotencyKey, tags[] (ticketId, dept, notificationType).
Data contract — send response messageId (Mailjet id), status (sent | queued), acceptedAt.
Data contract — delivery webhook event (sent | delivered | bounce | blocked | spam | open | click), messageId, email, time, reason, ticketId (from tags).
Error handling & SLA 2xx → ok; 422 (validation) → no retry, log; 5xx/429 → retry per §11.2. Bounces and blocks update the recipient's email_deliverable flag and trigger fallback to SMS/in-app per the notifications pipeline. Target p95 ≤ 4 s send ack; delivery event freshness ≤ 5 min.
Assumptions / dependencies Mailjet account provisioned; sender domains verified; reply-to routing configured (Mailjet routes a per-ticket inbound address to SITP's inbound webhook). Transactional templates pre-approved (no marketing content).
Status contracted (Mailjet is the locked SMTP/email provider per _context.md §3).

Sample request — send templated email

POST /api/v1/integrations/mailjet/send
Authorization: Bearer <service JWT>
Idempotency-Key: 7f3c1a2e-9b44-4d21-8e6a-2c9b1f4d0a55
Content-Type: application/json

{
  "to": [{ "email": "primary.rep@acme.com.pk", "name": "Ayesha Khan" }],
  "from": { "email": "no-reply@sindhitportal.maahir.io", "name": "Sindh IT Portal" },
  "replyTo": { "email": "ticket-SITP-2026-ITD-000045@inbound.sindhitportal.maahir.io" },
  "templateId": 4821103,
  "variables": {
    "locale": "en",
    "ticketId": "SITP-2026-ITD-000045",
    "subject": "Your ticket has been assigned",
    "bodyMarkdown": "Ticket SITP-2026-ITD-000045 has been assigned to the IT Department...",
    "gregorianDate": "17 July 2026",
    "hijriDate": "2 Muharram 1448",
    "dept": "S&ITD",
    "portalUrl": "https://sindhitportal.maahir.io/tickets/SITP-2026-ITD-000045"
  },
  "tags": ["ticket:SITP-2026-ITD-000045", "type:assigned", "dept:ITD"],
  "channel": "email"
}

Sample response

HTTP/1.1 202 Accepted
Content-Type: application/json

{
  "messageId": "1948234751920384",
  "status": "queued",
  "acceptedAt": "2026-07-17T09:14:22.481Z",
  "adapter": "mailjet",
  "intCallId": "int_call_01HZX9F8K7P4N2Q3R6STV8WXY"
}

5.2 SMS gateway — Jazz / Telenor bulk SMS

Field Value
Purpose OTP delivery (2FA fallback for company reps), short ticket notifications (created, assigned, escalated, resolved), and escalation alerts to staff.
Direction Bidirectional (outbound send; inbound delivery reports).
Auth method API key + sender ID; credentials in the vault. DLT / PEPRA / Pakistan regulatory compliance: sender ID pre-approved; transactional vs promotional traffic separated; opt-out respected for any promotional class (SITP sends transactional only).
Key operations sendSms(...), inbound onDeliveryReport.
Data contract — send request to (E.164 MSISDN, validated), from (approved sender ID, e.g., SITP), body (concatenation-aware, locale-correct, no Urdu-in-SMS-7-bit pitfalls — Sindhi/Urdu trigger UCS-2 with the 70-char segment limit), idempotencyKey, tags[].
Data contract — send response messageId (gateway id), segmentCount, status (accepted | rejected).
Data contract — delivery report messageId, msisdn, status (delivered | failed | pending), errorCode, deliveredAt.
Error handling & SLA 2xx → accepted; 4xx (invalid number, sender-ID rejected) → no retry; 5xx → retry per §11.2. Delivery failures update the recipient's sms_deliverable flag and trigger fallback to email/in-app. Target p95 ≤ 3 s send ack; delivery report freshness ≤ 10 min. Failover between Jazz and Telenor is configurable per message class.
Assumptions / dependencies Bulk-SMS account with Jazz and/or Telenor; sender ID approved by the PTA-registered aggregator; UCS-2 support confirmed for Sindhi.
Status contracted (stack-locked SMS providers).

5.3 WhatsApp Business API — Cloud API

Field Value
Purpose Two-way chat with company reps; templated notifications (ticket created/assigned/resolved, MoM published); inbound replies append to a ticket thread; opt-in/opt-out management.
Direction Bidirectional (outbound template + session messages; inbound reply webhook).
Auth method WhatsApp Business Cloud API — System-User access token (graph API) with the whatsapp_business_messaging permission; phone-number ID and WABA ID in the vault. App secret used to verify the inbound webhook X-Hub-Signature-256 (HMAC-SHA256).
Key operations sendTemplate(...), sendText(...) (within 24-hour customer-service window), markMessageRead, inbound onMessage (text/media/voice), onMessageStatus, optIn/optOut.
Data contract — send template request to (E.164), templateName (pre-approved, e.g., ticket_assigned_en), language (en | ur | sd), components[] (header/body parameters: ticketId, dept, status, portalUrl), idempotencyKey.
Data contract — send response messageId (WA id), status (queued).
Data contract — inbound webhook from, messageId, type (text | image | document | voice), text / media ref, timestamp, replyContext (the ticket ref parsed from the most-recent outbound message).
Error handling & SLA 2xx → ok; 4xx (template not approved, recipient opted out, outside 24h window for non-template) → no retry, log + notify dispatcher to fall back; 5xx/429 → retry per §11.2. Recipient opt-out is honored immediately and propagated to the preference center. Target p95 ≤ 5 s send ack; inbound freshness ≤ 30 s.
Assumptions / dependencies WhatsApp Business Account approved; templates pre-approved by Meta for EN/UR/SD; display phone number verified; explicit opt-in recorded per recipient before any template send (compliance).
Status planned — WABA + template approvals required.

6. Video Conferencing — Zoom / Google Meet / Microsoft Teams

Field Value
Purpose Create a virtual meeting per TRI/hearing (Company + S&ITD + Department) when a physical-only meeting is not required; generate a join link for participants; pull the recording and transcript after the meeting for the MoM extraction pipeline.
Direction Outbound (create meeting, fetch recording/transcript); Inbound (optional recording-ready webhook).
Auth method OAuth 2.0 with each provider (Zoom OAuth + Server-to-Server; Google Workspace domain-wide delegation; Microsoft Teams application permissions). OAuth tokens + refresh tokens in the vault; refreshed by a scheduled job.
Key operations createMeeting(...), getJoinLink(meetingId), getRecording(meetingId), getTranscript(meetingId), inbound onRecordingReady.
Data contract — create request provider (zoom | meet | teams), topic (e.g., TRI — SITP-2026-ITD-000045), startTime (UTC), durationMinutes, agenda (pre-filled by AI from ticket history), participants[] (emails — invited via the provider), hostEmail (the S&ITD facilitator), record (boolean), transcribe (boolean, gated by feature flag).
Data contract — create response provider, meetingId, joinUrl, hostUrl, password (if any), calendarEventId.
Data contract — recording-ready webhook meetingId, provider, downloadUrl (presigned, short-lived), transcriptUrl, durationSeconds, sourceRef.
Error handling & SLA 2xx → ok; 401 → token refresh then retry once; 4xx → no retry; 5xx → retry per §11.2. Provider selection is configurable per meeting (a S&ITD facilitator may pick Zoom today and Teams tomorrow). Recordings are pulled (SITP fetches) not pushed, so a missing recording triggers a poll fallback. Target p95 ≤ 6 s for createMeeting.
Assumptions / dependencies OAuth apps registered with Zoom, Google, and Microsoft; recording and transcription features enabled per account; recordings downloaded and stored in MinIO (then deleted from the provider if the data-classification policy requires). Transcript text is fed to the MoM action-item extractor (AI capability #11).
Status planned — OAuth app registration required for each provider.

7. Identity & SSO — OIDC via Keycloak

Field Value
Purpose Government staff federated login via OIDC SSO so S&ITD and department staff sign in with their existing government identity (federated IdP) instead of a new SITP-local password; map group/role claims from the IdP into SITP roles; enforce 2FA for staff. Company representatives / citizens log in via the SITP-local Keycloak realm (email/password + 2FA optional).
Direction Bidirectional (SITP ↔ Keycloak; Keycloak ↔ government IdP).
Auth method OIDC Authorization Code flow with PKCE; short-lived access tokens (minutes) + refresh tokens (days), revocable via a denylist mirrored to Redis. Client secrets in the vault.
Key operations authorize, token, userInfo, refresh, logout, introspect.
Data contract — token claims sub, email, name, locale, realm (gov-staff | company), groups[] (from IdP, e.g., SITD-Staff, LBR-Section), roles[] (mapped from groups via Keycloak role-claim mapper → SITP roles: Staff, POC, DG, Secretary, SuperAdmin), deptCode, 2fa_verified (boolean).
Data contract — RBAC mapping A Keycloak mapper translates IdP groups into SITP roles; SITP's RolesGuard/PermissionsGuard consumes the role claims; granular per-permission overrides live in SITP (not Keycloak).
Error handling & SLA 401 → prompt re-auth; 403 → role/permission denied (logged); token-introspect failures fail closed (deny). Keycloak is treated as a critical dependency — health-checked continuously.
Assumptions / dependencies Keycloak self-hosted (stack-locked); government IdP federation requires coordination with the government identity authority (e.g., NADRA FBR-style staff IdP or departmental AD). Until federation is in place, staff use Keycloak-local accounts with mandatory 2FA.
Status contracted (Keycloak stack-locked); federation path planned pending IdP agreement.

8. Public API (for consumers)

The SITP exposes a versioned REST API at /api/v1 for authorized partner consumers — other government portals, integration partners, and large company tenants that want to file and track tickets programmatically. The public API is documented as OpenAPI 3.1, served at /api/v1/openapi.json and rendered at /docs/api.

8.1 Conventions

Concern Standard
Versioning URL-segment versioning (/api/v1, /api/v2). Breaking changes require a new major version; old version supported in parallel for ≥ 12 months.
Auth OAuth 2.0 client-credentials flow for machine-to-machine partners (client_id + client_secret → bearer access token, ≤ 1 hour TTL); optional partner API keys for read-only public resources. Tokens are scope-limited (tickets:write, tickets:read, kb:read, stats:read).
Rate limits Per-state (configurable in int_ratelimit): default 600 req/min per partner for read; 60 req/min for ticket creation. 429 returned with Retry-After. Bursty partners are throttled, never banned without notice.
Pagination Cursor-based (?cursor=...&limit=50, max limit=100) for list endpoints; total counts returned in a meta block where computable cheaply.
Filtering Query-string filters (?status=...&dept=...&since=...); filters whitelist-validated server-side.
Errors RFC 7807 application/problem+json with type, title, status, detail, instance, plus a SITP-specific code (e.g., SITP-VALIDATION-001).
Idempotency All POST/PUT accept Idempotency-Key; the same key within the 24-hour window returns the original response.
Localization `Accept-Language: en

8.2 Key resources

Resource Methods Scope Notes
/partners/tokens POST Exchange client credentials for a bearer token.
/tickets GET, POST tickets:read, tickets:write Authorized partners create/track tickets on behalf of a company (with that company's companyId).
/tickets/{ticketId} GET tickets:read Single ticket with status, SLA, history (role-filtered).
/tickets/{ticketId}/messages GET, POST tickets:write Append a public message; list messages.
/kb/articles GET kb:read Public KB articles (no auth required for public subset).
/stats/public GET stats:read or public Public transparency dashboard aggregates (anonymized).
/departments GET public Department directory + service catalog.
/service-catalog GET public Services offered with categories, SLAs, and required fields.
/webhooks/subscriptions GET, POST, DELETE webhooks:manage Manage outbound webhook subscriptions (see §9).

8.3 Example — create a ticket (partner)

Request

POST /api/v1/tickets
Authorization: Bearer <partner bearer token>
Idempotency-Key: 9a2c4e6b-1f3d-4a2c-9e8b-7d6c5b4a3f2e
Content-Type: application/json
Accept-Language: en

{
  "companyId": "cmp_01HZX7K4P9N2Q3R6STV8WXYPJ",
  "title": "Pending sales tax refund for Q4 2025",
  "description": "Our company filed the Q4 2025 SRB refund on 15 Jan 2026; no acknowledgment received.",
  "category": "SRB-REFUND",
  "targetDept": "SRB",
  "priority": "normal",
  "language": "en",
  "attachments": [
    { "filename": "SRB-Q4-2025-filing.pdf", "mimeType": "application/pdf", "size": 482310,
      "uploadId": "fil_upl_01HZX9F8K7P4N2Q3R6STV8WXY" }
  ],
  "requestedBy": { "repId": "rep_01HZX7K4P9N2Q3R6STV8WXYPJ" }
}

Response

HTTP/1.1 201 Created
Content-Type: application/json
Location: /api/v1/tickets/SITP-2026-SRB-000128

{
  "ticketId": "SITP-2026-SRB-000128",
  "status": "new",
  "state": "triage-pending",
  "category": "SRB-REFUND",
  "targetDept": "SRB",
  "priority": "normal",
  "sla": { "responseBy": "2026-07-19T09:00:00Z", "resolveBy": "2026-07-27T09:00:00Z" },
  "createdAt": "2026-07-17T09:18:42.117Z",
  "portalUrl": "https://sindhitportal.maahir.io/tickets/SITP-2026-SRB-000128",
  "attachments": [
    { "filename": "SRB-Q4-2025-filing.pdf", "attachmentId": "fil_att_01HZX9F8K7P4N2Q3R6STV8WXZ" }
  ]
}

8.4 Errors (RFC 7807)

HTTP/1.1 422 Unprocessable Entity
Content-Type: application/problem+json

{
  "type": "https://sindhitportal.maahir.io/docs/errors/validation",
  "title": "Validation failed",
  "status": 422,
  "detail": "companyId does not match the partner's authorized scope.",
  "instance": "/api/v1/tickets",
  "code": "SITP-VALIDATION-042",
  "errors": [{ "field": "companyId", "code": "SCOPE_MISMATCH" }]
}

9. Webhooks

Webhooks let partners react to SITP events in real time (outbound) and let external systems push events to SITP (inbound).

9.1 Outbound webhooks

Event Trigger Typical consumer
ticket.created New ticket filed by/for a partner's company Partner CRM
ticket.assigned Ticket assigned to a department Partner
ticket.status_changed Any status transition (triaged, in-progress, resolved, closed, reopened, appealed) Partner
ticket.escalated Escalation ladder step fired Partner + oversight dashboard
mom.published MoM published on a ticket Partner
ticket.resolved Resolution-proof gate satisfied Partner
ticket.closed Auto-close or company-accept Partner

Delivery contract:

Sample outbound webhook payload (signed)

POST https://partner.example.org/sitp/webhook
Content-Type: application/json
X-SITP-Event: ticket.status_changed
X-SITP-Signature: t=1752752322,v1=5b1c3729d4f8a2e6b0c1d9e7f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2
X-SITP-Delivery: dlv_01HZX9F8K7P4N2Q3R6STV8WXZ

{
  "eventId": "evt_01HZX9F8K7P4N2Q3R6STV8WXAB",
  "eventType": "ticket.status_changed",
  "occurredAt": "2026-07-17T09:38:42.000Z",
  "version": "1",
  "data": {
    "ticketId": "SITP-2026-SRB-000128",
    "companyId": "cmp_01HZX7K4P9N2Q3R6STV8WXYPJ",
    "previousStatus": "new",
    "status": "assigned",
    "assignedTo": { "dept": "SRB", "section": "Refunds" },
    "sla": { "responseBy": "2026-07-19T09:00:00Z" },
    "portalUrl": "https://sindhitportal.maahir.io/tickets/SITP-2026-SRB-000128"
  },
  "delivery": { "deliveryId": "dlv_01HZX9F8K7P4N2Q3R6STV8WXZ", "attempt": 1 }
}

The signature is computed as HMAC_SHA256(secret, "1752752322." + rawBody) and sent hex-encoded as v1. Partners read t from the header, reject if abs(now - t) > 300, then recompute the HMAC over t + "." + rawRequestBody and compare. The data block is versioned (version: "1") so the shape can evolve without breaking existing parsers; a major change ships as version: "2" with a parallel delivery until partners migrate.

9.2 Inbound webhooks

Inbound webhooks are how external systems push events to SITP. The webhook receiver is the single front door for all of them: it verifies signatures, rejects replays, normalizes into an internal event, and enqueues to a BullMQ queue for a worker to dispatch into the right domain module.

Source Event Domain effect
Mailjet delivered, bounce, blocked, spam Update recipient deliverability flags.
Mailjet (inbound parse) Reply email Append to the ticket parsed from the reply-to address; notify watchers.
WhatsApp Cloud API Inbound message / status Append reply to ticket; update delivery status.
SMS gateway Delivery report Update sms_deliverable flag.
NITB e-Office onStatusChange Update ticket's e-Office file status; show "under official process".
Video provider Recording ready Trigger recording fetch → MoM pipeline.
Partner (push-back) Acknowledgment, custom status Update partner integration record.

Every inbound payload is recorded (sensitive fields redacted) in int_call with direction inbound, source, signature-verification result, and the dispatch outcome.


10. Data Mapping & Master Data

Each external system speaks its own field names and code sets; SITP keeps one canonical model and translates at the adapter boundary.

10.1 Master-data mappings

Mapping Source SITP canonical Notes
Department code External (e-Office, IdP group names) dept_code (e.g., ITD, LBR, SRB) Maintained in int_dept_map; one direction = lookup, the other = inverse-lookup.
Service/category code Service catalog (internal) ↔ external dept categories category_code (e.g., SRB-REFUND) Drives auto-routing.
Status codes Each adapter's status vocabulary SITP ticket lifecycle enum e-Office statuses (submitted, under-process, approved, returned, rejected) map to ticket state extensions, not replacements.
Locale ISO 639-1 (en, ur, sd) Same Used in templates and AI translation.
Calendar Gregorian (storage) ↔ Hijri (display) Both Computed at presentation; never stored twice.

10.2 Idempotency & reconciliation


11. Non-Functional Requirements for Integrations

These NFRs apply to every adapter and are the operational contract for the integration layer. Authoritative NFRs for the whole system are in 03-non-functional-requirements/en.md; the items here are integration-specific refinements.

ID Concern Target
NFR-INT-001 Timeout (default) 10 s connect + response per call, configurable per adapter (e.g., NADRA 8 s, Mailjet 4 s, SMS 3 s, e-Office push 10 s).
NFR-INT-002 Retries Max 5 attempts with exponential backoff + jitter: 1s, 4s, 16s, 60s, 240s (capped). 4xx (non-401/429) are not retried.
NFR-INT-003 Circuit-breaker thresholds Open after 5 consecutive failures or > 50% failure rate over 30 s; half-open after 60 s; close after 5 successful half-open calls. Per adapter.
NFR-INT-004 Idempotency window 24 hours; keyed by SITP-generated UUID or vendor idempotency token; responses cached and replayed on key match.
NFR-INT-005 DLQ Per-adapter dead-letter queue; alert fires on any DLQ entry; ops dashboard + replay UI; max retention 30 days then exported.
NFR-INT-006 Availability Integration layer ≥ 99.9% measured from inside SITP; per-adapter availability depends on the vendor and is tracked separately in the status page.
NFR-INT-007 Secrets rotation Provider credentials rotated every 90 days (or per vendor policy, whichever is shorter); rotation is a documented runbook; OAuth tokens refreshed automatically; rotation events audited.
NFR-INT-008 Observability per integration Each call emits an OTel span tagged with adapter, operation, outcome; p50/p95/p99 latency, error rate, and DLQ depth dashboards per adapter in Grafana; alerts on latency and error anomalies.
NFR-INT-009 Audit retention int_call rows retained per the data-classification policy (default 2 years hot, archived thereafter); sensitive fields redacted at write time.
NFR-INT-010 Data residency Sovereign-data adapters (NADRA, CNIC-bearing) are restricted to on-prem or vendor-approved channels; payloads are Restricted data class.

12. Integration Roadmap

Integrations are sequenced by dependency (MoU/access) and value (what unblocks the file-first registration flow and the ticket lifecycle). Phase mapping aligns with /specs/en/14-roadmap-release/.

12.1 Phase 1 — MVP core (must-have at launch)

Integration Why Phase 1 Dependency
Mailjet (email) Primary notification channel; transactional flow depends on it. Mailjet account (✅ stack-locked); sender-domain verification.
SMS gateway (Jazz/Telenor) OTP + critical notifications. Bulk-SMS account; sender-ID approval.
Keycloak OIDC (local + staff 2FA) Authentication backbone. Self-hosted (✅ stack-locked).
Public REST API v1 (tickets + KB + stats) Partner onboarding and transparency. None external.

12.2 Phase 2 — Registry verification wave (file-first depends on these)

Integration Why Phase 2 Dependency
NADRA Verisys Rep identity verification — backs Verified badge. MoU with NADRA; IP whitelisting.
SECP Company lookup — backs Verified badge. MoU / API access with SECP.
FBR (NTN + filer) Tax profile — backs Verified badge. MoU / API access with FBR.
SRB (STRN) Provincial priority for Sindh IT companies. MoU / API access with SRB.
PSEB Membership trust signal. Data-sharing agreement with PSEB.
WhatsApp Business API Two-way chat + templated notifications. WABA approval + template approvals.

12.3 Phase 3 — File movement & rich collaboration

Integration Why Phase 3 Dependency
NITB e-Office Official file movement — inter-departmental legitimacy. MoU with NITB; e-Office service account.
Video providers (Zoom/Meet/Teams) Hybrid TRI/hearing virtual meetings. OAuth app registration per provider.
Outbound webhooks Partner event delivery. None external (consumer-driven).
OIDC federation to government IdP Staff single sign-on. Government IdP agreement.

12.4 Phase 4+ — deferred / exploratory

12.5 Dependency summary

Dependency Type Blocks
NADRA Verisys MoU Governance Rep identity verification.
SECP API access Governance Company verification.
FBR API access Governance Tax verification.
SRB API access Governance STRN verification (provincial).
PSEB data-sharing Governance Membership validation.
NITB e-Office MoU + service account Governance + technical Official file movement.
Mailjet sender-domain verification Technical Outbound email reputation.
SMS sender-ID (PTA) approval Regulatory SMS delivery.
WhatsApp WABA + templates Vendor WhatsApp notifications + two-way chat.
Government IdP federation Governance Staff SSO.
Zoom/Google/Microsoft OAuth apps Vendor TRI/hearing video.

Until each dependency closes, the corresponding adapter ships dark behind a feature flag and the system degrades gracefully (e.g., verification returns "pending manual review"; notifications fall back to a working channel).


13. Open Questions / TBD

# Item Status
1 Exact API contract per government registry — most authorities publish only informal docs; formal specs are negotiated per MoU. TBD per MoU.
2 Whether NADRA Verisys supports biometric tokens over the API or only via dedicated devices. TBD with NADRA.
3 Choice of primary SMS aggregator (Jazz vs Telenor) and whether both are contracted for failover. TBD (procurement).
4 Whether e-Office integration fetches documents via presigned URL or requires base64 push (adapter supports both — finalize per NITB capability). TBD with NITB.
5 Government IdP for staff federation (existing departmental AD, or a new Sindh-wide IdP). TBD.
6 Public-API rate-limit ceilings per partner tier (default vs premium). TBD (commercial).
7 Retention period for int_call audit rows per data class. TBD with data-classification policy (see /specs/en/11-security-compliance/).

End of document.