> 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/design/architecture/services/user-preferences-service.md).

# User Preferences Service

## Overview

A lightweight Go microservice that manages per-user notification preferences and consent for the DIGIT platform. It stores which notification channels a user has opted into, their preferred language, and the scope of their consent.

### Key Functionalities

* **Per-channel consent** for WhatsApp, SMS, and Email (GRANTED / REVOKED)
* **Consent scoping** — `GLOBAL` (applies everywhere) or `TENANT`-specific
* **Language preference** — stores the user's preferred locale (e.g., `en_IN`, `hi_IN`, `fr_IN`, `pt_IN`)
* **Upsert semantics** — single `_upsert` endpoint handles both create and update
* **JSONB storage** — flexible payload structure, no schema migrations needed for changes
* **Auto-migration** — database table and indexes created automatically on startup

## Pre-requisites

Before you proceed with the configuration, make sure the following prerequisites are met:

* Go 1.23+
* PostgreSQL 12+

## Data Model

**Table:** `user_preference`

<table><thead><tr><th width="222.98046875">Column</th><th width="227.625">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>id</code></td><td>UUID</td><td>Primary key</td></tr><tr><td><code>user_id</code></td><td>VARCHAR(64)</td><td>User's UUID</td></tr><tr><td><code>tenant_id</code></td><td>VARCHAR(64)</td><td>Tenant context (nullable for global)</td></tr><tr><td><code>preference_code</code></td><td>VARCHAR(128)</td><td>Preference category</td></tr><tr><td><code>payload</code></td><td>JSONB</td><td>Consent and language data</td></tr><tr><td><code>created_by</code> / <code>last_modified_by</code></td><td>VARCHAR(64)</td><td>Audit fields</td></tr><tr><td><code>created_time</code> / <code>last_modified_time</code></td><td>BIGINT</td><td>Epoch timestamps</td></tr></tbody></table>

**Unique constraint:** `(user_id, COALESCE(tenant_id, ''), preference_code)`

#### Consent Payload Structure

```
{
  "preferredLanguage": "en_IN",
  "consent": {
    "WHATSAPP": { "status": "GRANTED", "scope": "GLOBAL" },
    "SMS": { "status": "GRANTED", "scope": "TENANT", "tenantId": "pg.citya" },
    "EMAIL": { "status": "REVOKED", "scope": "GLOBAL" }
  }
}
```

<table><thead><tr><th width="190.46875">Field</th><th width="261.984375">Values</th><th>Description</th></tr></thead><tbody><tr><td><code>status</code></td><td><code>GRANTED</code> / <code>REVOKED</code></td><td>Whether user has opted in</td></tr><tr><td><code>scope</code></td><td><code>GLOBAL</code> / <code>TENANT</code></td><td>Consent scope</td></tr><tr><td><code>tenantId</code></td><td>string (optional)</td><td>Required when scope is <code>TENANT</code></td></tr><tr><td><code>preferredLanguage</code></td><td><code>en_IN</code>, <code>hi_IN</code>, <code>fr_IN</code>, <code>pt_IN</code></td><td>User's locale for notifications</td></tr></tbody></table>

### API Endpoints

**Base path:** `/user-preference/v1`

<table><thead><tr><th width="184.07421875">Endpoint</th><th width="225.65625">Method</th><th>Description</th></tr></thead><tbody><tr><td><code>/v1/_upsert</code></td><td>POST</td><td>Create or update a preference</td></tr><tr><td><code>/v1/_search</code></td><td>POST</td><td>Search preferences by criteria</td></tr><tr><td><code>/health</code></td><td>GET</td><td>Health check</td></tr></tbody></table>

#### Upsert

```
curl -X POST "http://<host>/user-preference/v1/_upsert" \
  -H "Content-Type: application/json" \
  -d '{
    "RequestInfo": {
      "userInfo": { "uuid": "user-uuid", "tenantId": "pg.citya" }
    },
    "preference": {
      "userId": "user-uuid",
      "tenantId": "pg.citya",
      "preferenceCode": "USER_NOTIFICATION_PREFERENCES",
      "payload": {
        "preferredLanguage": "en_IN",
        "consent": {
          "WHATSAPP": { "status": "GRANTED", "scope": "GLOBAL" },
          "SMS": { "status": "REVOKED", "scope": "GLOBAL" },
          "EMAIL": { "status": "REVOKED", "scope": "GLOBAL" }
        }
      }
    }
  }'
```

#### Search

```
curl -X POST "http://<host>/user-preference/v1/_search" \
  -H "Content-Type: application/json" \
  -d '{
    "RequestInfo": {},
    "criteria": {
      "userId": "user-uuid",
      "tenantId": "pg.citya",
      "preferenceCode": "USER_NOTIFICATION_PREFERENCES"
    }
  }'
```

### How Novu-bridge Uses This Service

1. Fetches user's `preferredLanguage` to resolve locale-specific templates
2. Checks `consent.WHATSAPP.status` — if `GRANTED`, notification proceeds; if `REVOKED`, it's skipped
3. Logs skipped notifications as `SKIPPED` in the dispatch log

## Setup

#### Running Locally

```
# Create database (table auto-created on startup)
createdb user_preferences

# Run
go run cmd/server/main.go
```

Or with Docker:

```
docker-compose up -d
```

#### Configuration

| Variable              | Default            | Description       |
| --------------------- | ------------------ | ----------------- |
| `SERVER_PORT`         | `8080`             | HTTP port         |
| `SERVER_CONTEXT_PATH` | `/user-preference` | API context path  |
| `DB_HOST`             | `localhost`        | PostgreSQL host   |
| `DB_PORT`             | `5432`             | PostgreSQL port   |
| `DB_USER`             | `postgres`         | Database user     |
| `DB_PASSWORD`         | \`\`               | Database password |
| `DB_NAME`             | `user_preferences` | Database name     |
| `DB_SSL_MODE`         | `disable`          | SSL mode          |

#### Helm Chart

**Location:** [`deploy-as-code/helm/charts/common-services/digit-user-preferences-service`](https://github.com/egovernments/Citizen-Complaint-Resolution-System/tree/master/devops/deploy-as-code/charts/common-services/digit-user-preferences-service)

Refer to the link below for the API specs

* [OpenAPI Spec](https://github.com/egovernments/Citizen-Complaint-Resolution-System/blob/master/docs/WhatsApp_Bidirectional/API%20specifications/user-preferences.openapi.yaml)


---

# 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 dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.digit.org/complaints-management/design/architecture/services/user-preferences-service.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

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.
