> For the complete documentation index, see [llms.txt](https://docs.digit.org/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.digit.org/complaints-management/release-notes.md).

# Release Notes

Release notes for DIGIT Complaint Management v2.12

{% hint style="info" %}
**Release Date - September 25, 2026**
{% endhint %}

## Release Summary

DIGIT Complaint Management System v2.12 is the most significant release since we consolidated the repository. It expands the product from a conventional complaint management system into a configurable platform that supports multiple complaint, service-request, and case-management use cases.

#### At a glance

* **Upgrade path:** v2.11 to v2.12
* **New services:** a handful of new backend components shipped this release — see [New Services ](#new-services)for the complete, audited list (several dashboard/notification *features* described elsewhere are not separate services; that distinction is called out explicitly below).
* **Major feature:** complaint categories move from a fixed two-level list to a flexible tree; each city shapes itself — **breaking, and not automatic** for a city that already has categories set up. A validated migration tool now exists for this.

## Highlights

* Complaint categories are no longer fixed at two levels — configure any depth needed and assign to the right responsible units
* Resolvers and supervisors see preset indicators (volume, resolution time, status, geographic distribution) for their area, each clearly explained
* Automated SMS/WhatsApp/Email updates at key stages (registration, assignment, resolution, reopening) — rules, channels, and templates configurable without code
* Overdue complaints auto-escalate to the next responsible level or to supervisors for visibility; a tamper-evident audit trail logs every action
* Citizens can respond through the same update channel, letting governments collect follow-up info without breaking the complaint's thread
* Country-specific formats/validation for phone numbers, postal codes, names, and emails — no longer hardcoded to one geography; configurators can fetch geography info
* Get up and running much faster and consistently with an Ansible-based setup.

### What's New

**For citizens**

* **Journey-wide notifications** via SMS, WhatsApp, and email for filing, assignment, resolution, rating and escalation.
* **A simpler complaint form** with map-based location capture, improved category selection, country-specific validation and local-language error messages.
* **Auditable, automated escalation** when complaints exceed their resolution time.
* **Public Dashboard** — a curated, no-login view of selected complaint statistics.

**For employees and supervisors**

* **Complaint hierarchies of any depth**, aligned with the local government's service structure or citizen charter.
* Improved **employee inbox** with assignee filters, SLA visibility, reliable sorting and pagination, and optional My Complaints and All Complaints views.
* An evolved **Grievance routing officer** role to review incoming complaints and route them to the right department with human-in-the-loop mechanisms.
* **Personalised Supervisor Dashboard** with live counts, resolution times, charts, complaint maps and CSV export, scoped by jurisdiction and department.
* **Automatic escalation** of complaints that stay unresolved past their allowed time.
* A **working context switcher** in the top bar for employees holding more than one department/role.

**For city administrators (Admin Console / DIGIT Configurator)**

* **One-click geography setup** through OpenStreetMap, alongside Excel upload and map-based verification.
* Manage the new **multi-level complaint categories**, view the staff organisation chart, edit all languages side-by-side, and sync WhatsApp message templates with the provider.
* **Configurable reopening period** for resolved complaints, with a default window of five days.
* Choose what's public: pick which **Supervisor Dashboard** indicators show on the Public Dashboard.
* **Control access precisely:** configure the jurisdiction/department access policy for dashboards and search.
* **Expanded configuration tools** for complaint hierarchies, staff structures, multilingual content and WhatsApp templates.

**For the IT/operations team**

* **Local government deployment** using one settings file, with pre-flight checks before installation.
* **Automated test suites** are now available for the employee and citizen experiences.
* **System health monitoring built in.** Built-in observability through service logs, metrics, request traces and ready-made dashboards.
* **Stronger security and accountability** through tamper-evident audit trails, encrypted personal data and secure secrets management with OpenBao.
* Every service is built from one public source instead of a mix of locations; **Configurator and citizen/employee UI** are now built for two processor architectures.
* A **validated deployment path** for Windows via WSL2.
* **Platform version** upgraded to 2.9.3.

## New Services

The table below lists all new services added as part of this release.

<table><thead><tr><th width="231.49609375">New in v2.12</th><th>What it is</th></tr></thead><tbody><tr><td><strong>Configurator</strong> (Configurator, also called "Admin Console")</td><td>The web app a city admin uses to set up and configure a city — boundaries, complaint categories, staff, dashboards, notifications. Replaces manual/scripted city setup.</td></tr><tr><td><strong>digit-ui-esbuild</strong></td><td>The rebuilt citizen and employee web app.</td></tr><tr><td><strong>digit-mcp</strong></td><td>An automation toolkit that speeds up onboarding a new city. On by default.</td></tr><tr><td><strong>Real OTP delivery</strong> (otp-publisher)</td><td>Sends genuine one-time passwords for login, instead of the earlier test stub. Switched on with <code>enable_otp_services</code>.</td></tr><tr><td><strong>Notification admin dashboard</strong> (novu-dashboard) + <strong>novu-bridge-endpoint</strong></td><td>Lets a city manage its own notification (SMS/WhatsApp/Email) templates. Switched on with <code>enable_novu</code>.</td></tr><tr><td><strong>Location search</strong> (turbopass)</td><td>Address/place auto-complete when setting up city boundaries. Off by default (<code>enable_turbopass</code>).</td></tr><tr><td><strong>Host monitoring</strong> (node-exporter)</td><td>Server CPU/memory/disk metrics for the monitoring dashboards. Always on.</td></tr><tr><td><strong>Single-VM installer</strong> (local-setup, Ansible-driven)</td><td>Stands up a full deployment from one settings file and one command, with built-in pre-flight checks.</td></tr><tr><td><strong>Performance testing framework</strong> (<code>performance/</code>)</td><td>k6-based load tests used to validate this release at scale (up to 1M complaints / 500K+ daily transactions).</td></tr></tbody></table>

## New Features

### 1. Multi-Level Complaint Categories

Complaints are now classified using a category tree the city defines — for example, *Sanitation → Garbage → Missed collection* — instead of a fixed two-level list. Already-filed complaints keep working as long as the same category codes are reused.

<table><thead><tr><th width="142.73046875"></th><th></th></tr></thead><tbody><tr><td>Enable</td><td>Nothing to flip — this is the classification model in v2.12.</td></tr><tr><td>City data</td><td><code>RAINMAKER-PGR.ComplaintHierarchyDefinition</code> (the shape: how many levels, their names), <code>RAINMAKER-PGR.ComplaintHierarchy</code> (the actual category tree; a leaf's <code>code</code> <strong>is</strong> the complaint's <code>serviceCode</code>). Default: a 2-level tree (Category → Sub-type). Editable in DIGIT Studio.</td></tr><tr><td>Existing cities</td><td><strong>Action required</strong> — convert old <code>ServiceDefs</code> rows into the two new records, reusing leaf codes so existing complaints stay valid. Use the validated migration tool (<a href="https://github.com/egovernments/Citizen-Complaint-Resolution-System/blob/master/docs/migration/servicedefs-to-complainthierarchy-migration.md">servicedefs-to-complainthierarchy-migration.md</a>) rather than doing this by hand — migration guide Section 2.1.</td></tr><tr><td>Related</td><td>Migration <code>V20260731000000__repoint_grain_mvs_to_complainthierarchy.sql</code> (and, found during later validation, <code>V20260810000000__tenant_business_calendar_grains.sql</code>, which fixes report time-groupings to use each city's own time zone) repoint the reporting views to the new categories — run automatically.</td></tr><tr><td>Watch out for</td><td>Two gaps found during real-world validation of this migration: a complaint whose category hasn't been migrated yet will fail to send notifications on its next workflow action even though it still opens and displays fine; and the reporting views resolve a category's department by comparing across every city in the deployment rather than each city's own, so two cities sharing a default category code with different departments can end up showing one city's department on the other's dashboard tiles. See the migration tool's documentation for exposure-check queries.</td></tr></tbody></table>

### 2. Supervisor Dashboard

Live counts, resolution times, charts, and a complaint map. Each supervisor sees only their own area and department; access is role-based. Answers to "how many complaints, where, how fast resolved" are computed by a reporting/analytics engine that ships as part of the complaints service (refresh is on by default) — not a separate deployable component.

<table><thead><tr><th width="150.92578125"></th><th></th></tr></thead><tbody><tr><td>Enable</td><td>On a <strong>running</strong> deployment: <code>local-setup/scripts/enable-dashboard.sh</code> — registers the dashboard data definitions, loads 39 ready-made statistics + 1 dashboard pack (mapped to the roles that exist on that deployment), grants the menu entry, loads the dashboard's translations for every language, refreshes login tokens, and verifies end-to-end.</td></tr><tr><td>City data</td><td><code>dss.KpiDefinition</code>, <code>dss.DashboardPack</code>, <code>dss.DashboardConfig</code> (who can see which pages, number formats, department scoping).</td></tr><tr><td>Service settings</td><td><code>pgr.dashboard.refresh.enabled=true</code> (statistics refresh, on by default), <code>.interval.ms</code>; <code>pgr.analytics.config-cache-ttl-ms</code>.</td></tr><tr><td>Gateway</td><td>Whitelist <code>/v2/dashboard</code> <strong>and</strong> all of <code>/v2/analytics/_query</code>, <code>_schema</code>, <code>/packs</code>, <code>catalog/_search</code>.</td></tr><tr><td>Important caveat</td><td>The dashboard only shows a supervisor data for their own area <strong>and department</strong>, and errs on the side of showing nothing: <strong>an employee whose HR record has no department will see an empty dashboard.</strong> Check department assignments in HR data first; <code>dss.DashboardConfig.departmentScoping=false</code> turns the department restriction off city-wide.</td></tr><tr><td>Performance telemetry</td><td>The dashboard reports its own loading speed by default, which adds two public gateway routes (<code>/otel/v1/metrics</code>, <code>/otel/v1/logs</code>). <code>dashboard_metrics_enabled: false</code> stops the dashboard from <em>sending</em> this telemetry, but the two routes themselves stay in place — they are not removed by this setting.</td></tr><tr><td>An older dashboard</td><td>The pre-catalog dashboard that predates this KPI-catalog-driven Supervisor Dashboard is now hidden by default, kept only as a rollback aid.</td></tr></tbody></table>

### 3. Public Dashboard

A curated, no-login view of selected Supervisor Dashboard statistics for citizens and the public.

<table><thead><tr><th width="122.484375"></th><th></th></tr></thead><tbody><tr><td>Enable</td><td>City admin turns it on/off via a single toggle in the Admin Console (and gets its shareable URL); backed by <code>dss.DashboardConfig</code>. Which KPIs are exposed is fixed by the shipped <code>public-default</code> KPI pack (MDMS seed data), not a per-KPI admin choice today.</td></tr><tr><td>Access</td><td>Served without a login session, at its own web address. Deliberately excludes the schema-introspection endpoint (<code>_schema</code>) from its gateway whitelist, so a public caller can't discover column/data structure — only <code>/packs</code>, <code>/catalog/_search</code>, and <code>/_query</code> are exposed.</td></tr><tr><td>Isolation</td><td>A public caller cannot escape the curated set of indicators the city admin selected — verified by dedicated isolation tests.</td></tr></tbody></table>

### 4. Jurisdiction & Department-Based Access Control

Decides which rows of dashboard, analytics, and complaint-search/inbox data a given role/employee can see, based on their assigned area (jurisdiction) and department — replacing a set of hardcoded role exemptions with a configurable policy. **A plain-language explainer covering exactly how this works for complaint search and the employee inbox is at** [**jurisdiction-access-control**](/complaints-management/release-notes/jurisdiction-access-control.md)— read that first if you're configuring this for the first time.

<table><thead><tr><th width="152.1953125"></th><th></th></tr></thead><tbody><tr><td>Enable</td><td>Ships with the complaints service; the policy is per-tenant seed data (a scope-policy record keyed to a specific access-control action, with per-role scopes for jurisdiction and department, each <code>OWN</code> or <code>ALL</code>).</td></tr><tr><td>What it does</td><td>For roles without an explicit scope entry, a default policy applies. If an employee's HR record has no resolvable jurisdiction or department, that axis resolves to a deny-all rather than an unrestricted view — <strong>fails closed, not open</strong>.</td></tr><tr><td>Watch out for</td><td>An independent review of this feature found real gaps for admin-level roles: tenant-wide admin/supervisor roles can lose unrestricted dashboard access; a cross-tenant authorisation gap lets one tenant's role refresh another tenant's dashboard config cache; the "department scoping disabled" override can be silently skipped; and a fresh/source-less tenant bootstrap can end up with no row-scope policy seeded at all. <strong>Verify this specifically for any admin/supervisor role your city relies on before enabling.</strong> See <a href="#known-issues">Known Issues</a>.</td></tr></tbody></table>

### 5. Citizen Notifications&#x20;

Automatic messages to citizens (and staff) at each step of a complaint — filed, assigned, reassigned, resolved, rating requested, escalated. Which audience gets which message on which channel is configuration, not code.

<table><thead><tr><th width="158.23046875"></th><th></th></tr></thead><tbody><tr><td>Enable</td><td>(1) Deployment flag <code>enable_novu: true</code> (starts the notification services); (2) complaints-service setting <code>pgr.notification.config.driven=true</code> (deployment flag <code>pgr_notification_config_driven</code>); or simply run the guided installer <code>local-setup/scripts/enable-notifications.sh</code> on a running system. Verify with <code>local-setup/scripts/drive-test-complaint.py</code>.</td></tr><tr><td>City data</td><td><code>RAINMAKER-PGR.NotificationRouting</code> (who is notified, on which step, on which channel), <code>.NotificationTemplate</code> (the message wording, with placeholders), <code>.NotificationProviderTemplate</code> (maps each WhatsApp message to the provider's approved template ID).</td></tr><tr><td>WhatsApp — read this</td><td><strong>The sample WhatsApp template IDs shipped with the product belong to the reference demo account and will not work anywhere else.</strong> Each city must author its own WhatsApp message templates, get them approved (Meta approval via Twilio), and load <em>its own</em> template IDs — via the Admin Console's <strong>Sync WhatsApp templates</strong> screen or <code>local-setup/scripts/persist-provider-templates.py</code>. This is the single most common reason WhatsApp messages silently don't arrive. (Do <strong>not</strong> use the old <code>notif-mdms-seed/seed.sh</code> script — it loads data the current delivery path no longer reads.)</td></tr><tr><td>Channel defaults</td><td>The default channel is now <strong>SMS</strong> (previously WhatsApp), and <strong>WhatsApp is off until explicitly enabled</strong> (<code>novu.bridge.channels.enabled</code> defaults to <code>SMS,EMAIL</code>; WhatsApp events log as skipped until added).</td></tr><tr><td>Split-domain setups</td><td>Notification-dashboard URLs default to the city's own domain; if TLS terminates on a different public hostname set <code>novu_public_base_url</code> (per-URL overrides exist for rare cases).</td></tr><tr><td>Old messages</td><td>The previous fixed SMS wording (localisation keys) still works for cities that don't opt in — it is now deprecated.</td></tr></tbody></table>

### 6. Automatic Escalation&#x20;

A complaint that stays unresolved past its allowed time moves up automatically — for example, from the field worker to their supervisor — up to a configurable number of levels.

<table><thead><tr><th width="157.9453125"></th><th></th></tr></thead><tbody><tr><td>Enable/disable</td><td>On by default (<code>pgr.escalation.enabled=true</code>); per-deployment override <code>PGR_ESCALATION_ENABLED</code>.</td></tr><tr><td>City data</td><td><code>RAINMAKER-PGR.EscalationConfig</code> — how many levels (<code>maxDepth</code>), the time allowed per level, optional per-category overrides. Design default: 3 levels at 1 h / 4 h / 24 h — but as of this validation this master has <strong>no live seed path</strong>, so a fresh tenant doesn't actually receive it (the scheduler silently falls back to a hardcoded 5-day default when no record exists).</td></tr><tr><td>Service settings</td><td><code>pgr.escalation.interval.ms</code>, <code>.batch.size</code>, <code>.default.sla.ms</code>, <code>.max.depth</code>, <code>.kafka.topic=pgr-escalation-events</code> — <strong>this messaging topic must exist</strong> while escalation is on.</td></tr><tr><td>Prerequisite</td><td><code>Workflow.BusinessServiceMasterConfig</code> must contain a <code>PGR</code> row (<code>active:true, isStatelevel:true</code>).</td></tr></tbody></table>

### 7. Employee Inbox&#x20;

Adds two tabs to the employee inbox: complaints assigned to me (and my team), and all complaints I'm allowed to see, based on the HR reporting hierarchy. This is a **separate, additional** feature from the jurisdiction/department access control in Section 4 above — a complaint has to pass both to appear in a tab.

<table><thead><tr><th width="155.25"></th><th></th></tr></thead><tbody><tr><td>Enable</td><td><code>pgr.visibility.enabled=true</code> (env <code>PGR_VISIBILITY_ENABLED</code>) <strong>plus</strong> a per-city <code>RAINMAKER-PGR.InboxVisibilityConfig</code> record. Without the record the inbox behaves as before.</td></tr><tr><td>Service settings</td><td><code>pgr.visibility.hrms.employee.save.topic</code> / <code>.update.topic</code>, <code>.reportee.depth.default</code>, <code>.unassigned.states</code>, <code>.rebuild.cron</code>, <code>.rebuild.batch.size</code>, <code>.team.fanout.max</code>.</td></tr><tr><td>Endpoints</td><td><code>POST /v2/request/inbox/_search</code>, <code>POST /v2/request/inbox/_count</code> (whitelist if enabling).</td></tr><tr><td>Data</td><td>Uses a local copy of the HR reporting hierarchy (<code>eg_pgr_hrms_projection</code>, created by migration <code>V20260715000000</code>).</td></tr></tbody></table>

### 8. Country-Specific Form Checks

Forms now validate against the city's own country rules — phone-number format, postal-code format, name and email patterns — instead of a hardcoded single-country default. Error messages are generated from the rule and translated automatically.

<table><thead><tr><th width="181.7578125"></th><th></th></tr></thead><tbody><tr><td>Phone numbers</td><td><code>common-masters.MobileNumberValidation</code> — one record per country code with the number pattern. <strong>Exactly one record must be marked <code>default: true</code></strong> (the shipped record is not); otherwise the app quietly falls back to a built-in single-country pattern. Requires the 2.12 build of the User Service, which reads this record.</td></tr><tr><td>Postal code / name / email</td><td><strong>New record type <code>common-masters.FormValidations</code></strong> — one row per field (<code>postalCode</code>, <code>name</code>, <code>email</code>) with its pattern. This is the primary per-city rule and <strong>wins over</strong> the deployment-file fallback, in the Admin Console and all complaint forms. A default postal-code row is created for new cities — but the two seed paths that create it currently disagree (one ships a 5-digit pattern, the other a 6-digit pattern); reconcile before relying on a single format across fresh installs.</td></tr><tr><td>Deployment-file fallback</td><td><code>core_mobile_configs</code> and <code>core_postal_configs.postalCodePattern</code> are used only when the city record is absent. The old <code>postalCodeLength</code> / <code>postalCodeErrorMessage</code> settings are <strong>gone</strong> — the pattern is the single source, and the translated error message ("enter a valid N-digit postal code") is derived from it.</td></tr><tr><td>Existing cities</td><td><strong>Action required</strong> — the old <code>UserValidation</code> record type is removed (migration guide Section 2.2); cities created before v2.12 must add the <code>FormValidations</code> rows themselves.</td></tr></tbody></table>

### 9. Complaint Reopen Window

How long a citizen has to reopen a resolved complaint is now a real, editable city setting (default 72 hours). Previously, a hardcoded 1-hour limit applied no matter what was configured, and — until a later fix — the setting itself couldn't be edited at all through the Admin Console or API.

<table><thead><tr><th width="151.83203125"></th><th></th></tr></thead><tbody><tr><td>Config</td><td><code>RAINMAKER-PGR.UIConstants.REOPENSLA</code> (milliseconds; shipped default <code>259200000</code> = 72 hours) drives the reopen window in the citizen timeline and the employee/counter action bar alike. Edit per city in DIGIT Studio. Cities onboarded before this release keep their existing value.</td></tr><tr><td>Enforcement</td><td>The deadline is also enforced on the server from the same <code>REOPENSLA</code> master, based on the stored complaint (not what the request claims). Deployments must unset <code>time-before-closing-complaint</code>, which used to override it.</td></tr><tr><td>Fixed after 2026-08-25</td><td><code>REOPENSLA</code> originally couldn't be edited via the Admin Console/API at all — the master was keyed on the value itself, so any change was rejected outright. A database migration now re-keys it onto a stable identifier the first time a deployment upgrades; no action needed beyond letting that migration run.</td></tr></tbody></table>

### 10. Complaint Audit Trail

Every create/update of a complaint and every workflow step is recorded in a dedicated, signed audit log — who changed what, and when.

<table><thead><tr><th width="118.86328125"></th><th></th></tr></thead><tbody><tr><td>What</td><td>Complaint and workflow changes flow through the persister → audit-service into the <code>eg_audit_logs</code> table.</td></tr><tr><td>Config</td><td>Persister mappings carry <code>isAuditEnabled</code> + module (<code>CMS</code> / <code>Workflow</code>); the audit service reads the local persister configs (<code>EGOV_PERSIST_YML_REPO_PATH</code>) to decide what to audit, and binds <code>PERSISTER_AUDIT_KAFKA_TOPIC=audit-create</code>. Wired by default; no flag.</td></tr></tbody></table>

### 11. Employee Working Context

Lets an employee holding more than one department/role see and switch which one they are currently acting as from the top bar.

<table><thead><tr><th width="135.859375"></th><th></th></tr></thead><tbody><tr><td>Enable</td><td>Ships with the employee UI and complaints service; no flag.</td></tr><tr><td>Service settings</td><td><code>pgr.employee.context.resolver-role-codes</code> / <code>.citizen-role-codes</code> / <code>.admin-role-codes</code> (env <code>PGR_EMPLOYEE_CONTEXT_*</code>) — which roles the switcher treats as resolver / citizen-facing / admin. Defaults: <code>PGR_LME,GRO,DGRO</code> / <code>CITIZEN</code> / <code>PGR_ADMIN,SUPERUSER,MDMS_ADMIN,HRMS_ADMIN,STADMIN,SUPERVISOR,PGR_SUPERVISOR</code>.</td></tr></tbody></table>

### 12. Other Optional Add-Ons&#x20;

<table><thead><tr><th width="232.89453125">Flag</th><th width="231.265625">What you get</th><th>Notes</th></tr></thead><tbody><tr><td><code>enable_search_stack</code></td><td>Faster inbox search (Elasticsearch)</td><td>~3 GB extra RAM</td></tr><tr><td><code>enable_otp_services</code></td><td>Real one-time passwords for login</td><td>Replaces the built-in test OTP entirely; set <code>STATIC_OTP</code> for test environments</td></tr><tr><td><code>enable_digit_ui_v2</code></td><td>The new citizen web app at <code>/citizen/</code></td><td></td></tr><tr><td><code>enable_mcp</code> (+ <code>nginx_features.mcp</code>)</td><td>City-onboarding automation tools</td><td>Uses a ready-built image by default — works outside the internal network; <code>build_mcp: true</code> builds from source</td></tr><tr><td><code>enable_turbopass</code> / <code>enable_overpass</code></td><td>Address auto-complete / self-hosted boundary maps</td><td></td></tr><tr><td><code>enable_integration_tests[_runner]</code></td><td>Test dashboards + a run button</td><td></td></tr><tr><td><code>docker_log_max_size</code> / <code>docker_log_total_size</code></td><td>Caps on container log disk usage</td><td>Defaults 100 MB / 1 GB; deployments run outside Ansible get <strong>no</strong> log rotation</td></tr><tr><td><code>dashboard_metrics_enabled</code></td><td>Dashboard loading-speed telemetry</td><td>Default on; <code>false</code> stops the browser from <em>sending</em> this data, but does not remove the two <code>/otel/v1/*</code> gateway routes themselves</td></tr><tr><td><code>pgr_pincode_allowlist</code>, <code>login_tenant_allowlist</code>, <code>employee_module_denylist</code></td><td>Service-area and access lists</td><td>Leave <code>pgr_pincode_allowlist</code> unset (not <code>[]</code>) to accept any postal code</td></tr><tr><td><code>RAINMAKER-PGR.MapConfig</code> (city data)</td><td>Per-city map style, centre, and zoom</td><td>Optional; sensible defaults apply without it</td></tr><tr><td>Web analytics (Matomo)</td><td>Citizen-traffic analytics</td><td>Kubernetes/Helm chart only, not Compose</td></tr></tbody></table>

<details>

<summary><mark style="color:$primary;"><strong>New City Data &#x26; Settings Summary</strong></mark></summary>

<table><thead><tr><th width="197.609375">Record / key</th><th width="189.140625">Module</th><th width="216.828125">Default provided?</th><th>What it controls</th></tr></thead><tbody><tr><td><code>ComplaintHierarchyDefinition</code>, <code>ComplaintHierarchy</code></td><td>RAINMAKER-PGR</td><td>Yes (2-level tree)</td><td>Complaint categories (replaces <code>ServiceDefs</code>)</td></tr><tr><td><code>NotificationRouting</code>, <code>NotificationTemplate</code>, <code>NotificationProviderTemplate</code></td><td>RAINMAKER-PGR</td><td>Yes (demo WhatsApp template IDs — replace them!)</td><td>Citizen notifications</td></tr><tr><td><code>EscalationConfig</code></td><td>RAINMAKER-PGR</td><td>Yes (3 levels)</td><td>Automatic escalation timings</td></tr><tr><td><code>MapConfig</code></td><td>RAINMAKER-PGR</td><td>Schema only</td><td>Per-city map settings</td></tr><tr><td><code>InboxVisibilityConfig</code></td><td>RAINMAKER-PGR</td><td>Schema only</td><td>My/All inbox tabs</td></tr><tr><td><code>ComplaintExtendedAttributeSchema</code>, <code>ComplaintRelatedToMap</code>, <code>ComplaintTemplateType</code></td><td>RAINMAKER-PGR</td><td>Yes</td><td>Confidential/extended complaint fields</td></tr><tr><td><code>UIConstants.REOPENSLA</code></td><td>RAINMAKER-PGR</td><td>Yes (72 hours)</td><td>Complaint reopen window (now actually applied, UI + server)</td></tr><tr><td><code>MobileNumberValidation</code></td><td>common-masters</td><td>Yes (<code>default: false</code> — mark one record true)</td><td>Phone-number rules per country (replaces <code>UserValidation</code>)</td></tr><tr><td><code>FormValidations</code></td><td>common-masters</td><td>Yes (5-digit postal-code row)</td><td>Postal-code / name / email rules</td></tr><tr><td><code>KpiDefinition</code>, <code>DashboardPack</code>, <code>DashboardConfig</code></td><td>dss</td><td>Via <code>enable-dashboard.sh</code></td><td>Supervisor Dashboard</td></tr><tr><td><code>BusinessServiceMasterConfig</code> (+<code>PGR</code> row)</td><td>Workflow</td><td>Yes</td><td>Prerequisite for escalation &#x26; notification routing</td></tr><tr><td><code>SecurityPolicy</code> (expanded)</td><td>DataSecurity</td><td>Yes</td><td>Who may see personal data (name, phone, address…) — review before applying to live cities</td></tr><tr><td>New role <code>CMS_SCREENING_OFFICER</code></td><td>ACCESSCONTROL</td><td>Yes</td><td>Screening Officer: reviews and routes incoming complaints</td></tr></tbody></table>

</details>

## Changes

Below is the list of technical enhancements as part of this release:

* **Deployment parity:** Each service now runs its database migrations in its own init container, so Docker Compose and Kubernetes deployments behave the same way.
* **New CI checks:** Four new automated gates catch common issues before they ship — mismatched migrations/dumps, gateway whitelist inconsistencies, missing health checks, and outdated frontend lockfiles.
* **Postgres tuning:** Shared memory is now automatically sized based on your connection-pool settings, instead of needing manual tuning.
* **Log management:** Container logs are now capped and rotated automatically, so they don't fill up disk space.
* **Frontend caching:** The master-data cache in the browser moved to IndexedDB, so large cities with lots of data won't hit storage limits.
* **Session handling:** When a session expires, users now get a clean re-login prompt instead of the app hanging.
* **Dashboard auth:** The dashboard now has its own dedicated auth module with silent token refresh, which runs independently but alongside the main app's session.
* **Notification security:** Notification-service logs no longer expose personal data, and the diagnostics endpoint now requires authentication to access.
* **Input safety:** Email templates now automatically escape user-provided input, preventing injection issues.
* **Test suite flexibility:** The integration test suite no longer assumes a specific deployment setup — it now checks what capabilities are available and adjusts accordingly.

## Fixes

<table><thead><tr><th width="74.2265625">#</th><th width="179.69140625">Area</th><th>Fix</th></tr></thead><tbody><tr><td>1</td><td>Employee inbox</td><td>Complaint counts no longer stop at the page size; page navigation, column sorting, and time-allowed (SLA) calculations fixed (#1058, #1014, #1212, #1144)</td></tr><tr><td>2</td><td>Complaint updates</td><td>The chosen department is no longer overwritten on update; masked confidential values no longer wipe real data (#1077, #1092)</td></tr><tr><td>3</td><td>Citizen search</td><td>Citizens can only see their own complaints in search (#1100)</td></tr><tr><td>4</td><td>Dashboard statistics</td><td>"Complaints created today" now means the calendar day, not the last 24 hours (#1462, #1483)</td></tr><tr><td>5</td><td>Dashboard statistics</td><td>Reporting views now follow the new complaint categories; resolution-time targets fixed for city-level complaints (#1494, #1081)</td></tr><tr><td>6</td><td>Supervisor Dashboard</td><td>Broken header search removed; saved layouts no longer rearrange on refresh; date-filter no longer blanks the page (#1474, #1013)</td></tr><tr><td>7</td><td>Admin Console</td><td>Complaint-type edits no longer silently discarded; complaint workflow read from the correct city (#521)</td></tr><tr><td>8</td><td>Admin Console</td><td>City boundaries are saved to the city itself, not the state root</td></tr><tr><td>9</td><td>HR data</td><td>Employee-update crash fixed (#1056); a start-up race condition removed</td></tr><tr><td>10</td><td>Login &#x26; security</td><td>Admin account gets the correct encryption key after first-time setup (#1042); expired sessions no longer hang the app (#1101)</td></tr><tr><td>11</td><td>Complaint reopen</td><td>The reopen deadline is enforced on the server and follows the configured window (#925)</td></tr><tr><td>12</td><td>Postal codes</td><td>One postal-code rule everywhere, with translated error messages; raw browser pop-ups removed (#722, #1315)</td></tr><tr><td>13</td><td>Admin Console</td><td>A bug that incorrectly blocked legitimate users from editing MDMS roles/actions in the Admin Console is fixed</td></tr><tr><td>14</td><td>City/tenant setup</td><td>A tenant-setup failure partway through a multi-page data fetch no longer silently drops the master-data rows already fetched</td></tr><tr><td>15</td><td>Admin Console</td><td>Creating a new complaint category now correctly stamps the metadata it needs (previously could produce a category invisible to the citizen/employee picker)</td></tr><tr><td>16</td><td>Search</td><td>PGR search stays fully backward-compatible for a tenant that never configured a dashboard access-scope policy, instead of silently defaulting to an overly restrictive scope</td></tr><tr><td>17</td><td>Security &#x26; bootstrap</td><td>New-city bootstrap admin credentials no longer come from a fixed, shared set of environment variables — each bootstrap now uses the deploying session's own credentials</td></tr><tr><td>18</td><td>Security &#x26; bootstrap</td><td>A missing administrator role in the seeded roles master was restored</td></tr><tr><td>19</td><td>Security &#x26; bootstrap</td><td>A stale, unauthenticated gateway route to a decommissioned endpoint was dropped from both the Kong and Kubernetes gateway whitelists</td></tr><tr><td>20</td><td>Dashboard statistics</td><td>Report time-groupings (day/week/month/hour) now use each city's own time zone instead of one hardcoded zone (found during migration validation)</td></tr><tr><td>21</td><td>Complaint reopen</td><td>The reopen window (<code>REOPENSLA</code>) can now actually be edited via the Admin Console/API — previously blocked by a technical defect where the value doubled as the record's own identifier, so an edit silently failed to save</td></tr><tr><td>22</td><td>Access control</td><td>Fixed a severe scope mismatch: an employee with a coarse jurisdiction assignment (e.g. County-level, common for new employees) previously saw <strong>zero results in search</strong> but <strong>nearly the entire tenant on the dashboard</strong>, because the two features resolved jurisdiction differently. Both now expand a jurisdiction to itself plus every area beneath it, consistently</td></tr><tr><td>23</td><td>Access control</td><td>The Grievance Routing Officer role (and the unconfigured-role default) no longer sees every department's complaints regardless of their own assigned department — tightened to their own department, matching other supervisor-level roles</td></tr><tr><td>24</td><td>Admin Console</td><td>The Dashboard sidebar entry, corrupted in the sample seed data for one tenant, is restored — affected any city bootstrapped from that sample</td></tr><tr><td>25</td><td>Login &#x26; security</td><td>The seeded local admin account is granted the account-administrator permission it needs now that city-bootstrap actions require it; a missing Supervisor role was added to the seed data</td></tr><tr><td>26</td><td>Employee profile</td><td>A backend authentication/type-handling bug in the new employee working-context switcher is fixed</td></tr><tr><td>27</td><td>Notifications</td><td>A WhatsApp integration/provider-selection bug fixed</td></tr><tr><td>28</td><td>Employee &#x26; UI</td><td>Profile image visibility fixed; duplicate options removed from a dropdown; employee date-of-birth/date-of-appointment made optional where they shouldn't have been required</td></tr></tbody></table>

### Known Issues

Limitations to be aware of before adopting this release:

* **Windows support is WSL2-only.** Native Windows and Red Hat Linux are not yet supported.
* **The one-click installer does not currently work on AWS.** The Ansible-driven one-command install path fails when the target host is on AWS — use Docker Compose directly on an AWS host until this is fixed, or run the installer against a non-AWS host.
* **No automated ServiceDefs → ComplaintHierarchy migration for hand-converting a tenant.** The old `ServiceDefs` complaint-category master is removed; a validated migration *tool* now exists (see [servicedefs-to-complainthierarchy-migration.md](https://github.com/egovernments/Citizen-Complaint-Resolution-System/blob/master/docs/migration/servicedefs-to-complainthierarchy-migration.md)) and is the recommended path, but there is still no automatic, zero-touch conversion — someone must run it per tenant.
* **The new jurisdiction/department access control for dashboards and analytics needs care before enabling for admin roles.** An independent review found that tenant-wide admin/supervisor roles can lose unrestricted dashboard access and see it go empty, a cross-tenant config-refresh authorisation gap, and a case where the "department scoping disabled" override doesn't actually apply. If your city relies on admin roles seeing the full dashboard, verify this specifically before rollout.
* **The search/inbox filter panel isn't scoped, even though search results are.** The dropdown lists of jurisdictions and departments to filter by show every value in the tenant, not just the ones the logged-in employee is scoped to a UI defect. The scoping itself still works correctly; only the filter picker's contents are wrong. See [jurisdiction-access-control](/complaints-management/release-notes/jurisdiction-access-control.md) for the complete picture.
* **There's no way to revoke a jurisdiction/department once it's been granted to an employee.** The Configurator can add a jurisdiction or department assignment, but has no corresponding "remove" path today.
* **Migrating multiple cities that share common category codes can show one city's department on another's dashboard tiles**, because the reporting views resolve a category's department by comparing across the whole deployment rather than each city's own. Refer to the migration tool documentation for a query that checks your exposure.
* **A complaint whose category hasn't been migrated will fail to send notifications** on its next workflow action (assign/resolve/escalate), even though it still opens and displays fine.
* **The Department Grievance Routing Officer (DGRO) role gets the Dashboard sidebar link but not the underlying permission to use it.** DGRO's seeded role-actions include the nav-link action but none of the analytics capability actions, so a DGRO employee sees the Dashboard menu entry but every dashboard request is denied. Confirmed against the current seed data — grant DGRO the same capability actions as other supervisor-level roles if you need this role to actually use the dashboard.
* **Escalation can misfire after it triggers.** Once a complaint auto-escalates, completing the workflow from the escalated assignee's side doesn't always work correctly.
* **Employee-timeline PII masking isn't wired up yet.** The visibility grants described under [New City Data & Settings ](#new-city-data-and-settings-summary)(`DataSecurity.DecryptionABAC`) aren't actually applied on the employee timeline — confirmed open (#1970), consistent with the seed-data gap called out earlier in this document.
* **GRO↔LME assignment can assign the wrong people.** The assignee dropdown doesn't always filter correctly by role when assigning a complaint from GRO to LME (or back).
* **LME can occasionally get stuck unable to mark a complaint resolved** — root-caused to a specific caching issue rather than a broader workflow defect.
* **Several Supervisor Dashboard KPIs are wrong or unavailable**: SLA Non-Compliance Rate, Employees with Most Open Complaints, Employee Performance, and Complaints at Risk were the specific ones confirmed failing at sign-off.
* **Dashboard CSV export shows raw numbers instead of percentages** for percentage-based statistics (#1977).
* **WhatsApp/SMS can silently stop sending, with no error surfaced in the Configurator,** if the notification provider's (Twilio) token has expired — check the provider console before assuming a configuration problem.

<details>

<summary><mark style="color:$primary;"><strong>Open issues list - to be addressed in subsequent releases</strong></mark></summary>

Open items for this release (tracked under milestone "Release 2.12 (Nosy Build)"), planned for future updates:&#x20;

<table><thead><tr><th width="214.7734375">Area</th><th>Issue</th></tr></thead><tbody><tr><td>Notifications</td><td>Error while creating the workflow in Novu (#1517)</td></tr><tr><td>Notifications</td><td>Sync WhatsApp templates from Twilio fails for some messages (#1516)</td></tr><tr><td>Notifications</td><td>Configure Notifications: Delete and Recreate features not working as expected (#1501)</td></tr><tr><td>Notifications</td><td>Employees are not yet notified on complaint assignment across email, SMS and WhatsApp (#904)</td></tr><tr><td>Notifications</td><td><code>TemplateBinding</code> needs an <code>audience</code> selector and <code>body</code> field (blocks SMS work) (#905)</td></tr><tr><td>Notifications</td><td>Notification setup documentation for implementation teams still to be published (#1032)</td></tr><tr><td>Supervisor Dashboard</td><td>Employee dashboard: unable to add KPIs (#1276)</td></tr><tr><td>Supervisor Dashboard</td><td>Portuguese localisation and language dropdown driven by DIGIT-configured languages (#1108, #1169)</td></tr><tr><td>Supervisor Dashboard</td><td>Group-by across complaint hierarchy levels (Category / Sub-Type / Leaf) (#1111)</td></tr><tr><td>Supervisor Dashboard</td><td>Production render-lag instrumentation and load-time benchmarks at 1K / 50K / 100K records (#1110, #1109)</td></tr><tr><td>Supervisor Dashboard</td><td>Data dictionary asset creation for the CMS dashboard (#1575)</td></tr><tr><td>Complaint lifecycle</td><td>Reopen complaint timeline: increase to 72 hours and make it configurable (#1252)</td></tr><tr><td>Complaint lifecycle</td><td>Complaints at Risk: complaint ID click redirects to a wrong URL path (#1249)</td></tr><tr><td>Complaint lifecycle</td><td>Validate complaint types against the tenant master, dropping the state-level fallback (#902)</td></tr><tr><td>Configuration</td><td>Resolve the department master per tenant with state-level fallback (#901)</td></tr><tr><td>Deployment / operations</td><td>Ansible deploy: fixes needed for fresh-box and repeat deploys (#1245)</td></tr><tr><td>Deployment / operations</td><td>Image ↔ DB-migration version skew: floating tags can reintroduce login failures (#1023)</td></tr><tr><td>Deployment / operations</td><td>Default alert thresholds for Grafana (#1538); observability dashboards for operations &#x26; maintenance (#541)</td></tr><tr><td>Deployment / operations</td><td>Citizen login on the Docker Compose setup (#453)</td></tr><tr><td>Deployment / operations</td><td>Developer environment setup within an hour (#191);</td></tr><tr><td>Platform</td><td>Rate-limiter configuration (#1253); Matomo analytics enablement (#1254)</td></tr><tr><td>Quality &#x26; security</td><td>Vulnerability testing (#1482); testing-suite improvements (#1047)</td></tr></tbody></table>

</details>

#### Changes to be considered before upgrading

Most new capabilities are **switched off until an account chooses to turn them on**. An upgraded installation behaves like v2.11 until each feature is enabled (see [Turning Features On](#new-features)). A small number of changes require action from the operations team before upgrading.

* **Organise complaints in line with the structure in citizen charters.** Complaint categories are no longer fixed at two levels; governments can configure categories to the level of detail required, without being restricted to a fixed two-level hierarchy.
* **Monitor performance through a personalised dashboard.** Last-mile resolvers and supervisors see information relevant to their own area, including preset indicators on complaint volumes, resolution times, status and geographic distribution.
* **Share performance publicly.** A new, curated Public Dashboard lets a city publish selected statistics for citizens and the public, no login required.
* **Keep citizens informed using a powerful notification engine.** Automatic, timely updates through SMS, WhatsApp and Email at key stages of a complaint, configurable without code changes.
* **Make sure nothing slips through.** Overdue complaints escalate automatically, and a tamper-evident audit trail records every action and status change.
* **Deploy across countries with built-in internationalisation** — country-specific formats and validation rules for phone numbers, postal codes, names and emails.
* **Decide precisely who can see what.** A new access-control policy governs who can see which dashboard, analytics, and search results, by jurisdiction (geography) and department — replacing a handful of hardcoded role exemptions with a configurable, per-city policy. See [jurisdiction-access-control ](/complaints-management/release-notes/jurisdiction-access-control.md)for how this works for search and the employee inbox specifically.
* **Get up and running much faster and more consistently with Ansible-based setup.** One settings file per local government, one command, built-in pre-flight checks — supported on **Ubuntu** and **macOS**, with a validated quickstart for **Windows via WSL2** (see the [Windows quickstart guide](/complaints-management/deploy/setup/single-machine-setup/windows-quickstart.md)). Native Windows and Red Hat Linux are not yet explicitly supported.
* **One place for every image.** Every service build now comes from a single public source instead of a mix of internal and public locations; the Configurator and citizen/employee UI builds are now produced for both common processor architectures.
* **A working context switcher** for employees holding more than one department/role, so they can see and switch which one they're currently acting as.

## Related Docs

<table><thead><tr><th width="285.6640625">Document</th><th>Description</th></tr></thead><tbody><tr><td><a href="https://docs.digit.org/complaints-management/community/roadmap">Complaints Management roadmap</a></td><td>Product roadmap</td></tr><tr><td><a href="/complaints-management/release-notes/changelog.md">Changelog</a></td><td>Configuration &#x26; infrastructure changelog (city data, service settings, DevOps)</td></tr><tr><td><a href="/complaints-management/release-notes/migration-guide.md">Migration Guide</a></td><td>Operator upgrade procedure v2.11 → v2.12</td></tr><tr><td><a href="/complaints-management/release-notes/jurisdiction-access-control.md">Jurisdiction Access Control</a></td><td>Plain-language explainer: jurisdiction/department access control for complaint search &#x26; inbox</td></tr><tr><td><a href="/complaints-management/deploy/setup/single-machine-setup/windows-quickstart.md">Windows</a><a href="/complaints-management/deploy/setup/single-machine-setup/windows-quickstart.md"> Quickstart</a></td><td>Validated Windows setup path (via WSL2)</td></tr><tr><td><a href="/complaints-management/deploy/setup/single-machine-setup.md">Local Setup</a></td><td>City onboarding + add-ons catalogue (every optional flag)</td></tr><tr><td><a href="/complaints-management/access/configurator-management-guide/notifications.md">Notifications </a></td><td>Notifications setup guide (SMS, WhatsApp, email)</td></tr><tr><td><a href="/complaints-management/access/configurator-onboarding-guide.md">Configurator onboarding guide</a></td><td>setup and configure the complaint management system</td></tr><tr><td><a href="/complaints-management/access/configurator-management-guide.md">Configurator management guide</a></td><td>manage the complaint management system</td></tr><tr><td><a href="https://app.gitbook.com/o/-MEQmzNGXk5ajuZujG7E/sites/site_cjaZe/s/xNs29HvQfLFAPZCcDd7D/deploy/configure/dashboard">Dashboard configuration</a></td><td>configure and setup the dashboard</td></tr><tr><td><a href="https://app.gitbook.com/o/-MEQmzNGXk5ajuZujG7E/sites/site_cjaZe/s/xNs29HvQfLFAPZCcDd7D/deploy/setup/production-setup/deploy">Deploy</a></td><td>deploy Complaints Management System</td></tr><tr><td><a href="https://app.gitbook.com/o/-MEQmzNGXk5ajuZujG7E/sites/site_cjaZe/s/xNs29HvQfLFAPZCcDd7D/deploy/configure/notifications">Notifications</a></td><td>configure notifications</td></tr><tr><td><a href="/complaints-management/release-notes/ansible-performance-and-benchmarking.md">Performance &#x26; benchmarking</a></td><td>access the performance and benchmarking test reports</td></tr></tbody></table>


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.digit.org/complaints-management/release-notes.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
