> 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/health/design/architecture/low-level-design/services/platform-services/egov-notification-push.md).

# eGov Notification Push

## Overview

The **eGov Notification Push** service sends mobile push notifications in HCM. It delivers alerts like stock updates, task reminders, and workflow notifications to field devices through **Firebase Cloud Messaging (FCM)**.

The service handles two jobs:

1. **Maintain the device-token registry.** Mobile devices register their FCM token against a user, facility, and role set.
2. **Deliver push messages.** Upstream services publish notification requests, and this service resolves the target devices and sends the message through FCM.

{% hint style="info" %}
In short: *"who is on which phone, and push this message to them."*
{% endhint %}

### At a glance

* **Used by:** the mobile app and upstream notification producers.
* **Input:** device-token registrations, Kafka notification events, or direct send requests.
* **Output:** persisted device-token mappings and push delivery attempts through FCM.
* **Source of truth:** this service owns the device-token registry in `eg_push_device_tokens`.
* **Main paths:** register tokens, resolve audience, and send push messages.

## Dependencies

* **Firebase Cloud Messaging (FCM)** — the external Google service that actually delivers the push. Authenticated with the FCM service-account credentials (supplied as an environment value, never checked into the repo).
* **Kafka** — both the inbound notification-event topic and the outbound device-token persister topics.
* **egov-persister** (deployed via the `configs/` repo) — turns the device-token Kafka events into rows in Postgres; the service does not write the registry directly.
* **Postgres** — stores the device-token registry (table `eg_push_device_tokens`). Flyway migrations under `db/migration/main` create and evolve this table on start.
* **tracer** (`2.9.2-SNAPSHOT`) — correlation-id propagation, error handling, OpenTelemetry/Jaeger tracing.
* **Upstream callers** — health-notification-service (and any service emitting push events) are the producers of the inbound notification events; the mobile app is the producer of device-token registrations.

## Data Model

The service stores one main kind of state:

* device-token registrations

Each row links a device token to a user and its targeting context.

```sql
TABLE eg_push_device_tokens (
    id                VARCHAR(...) PRIMARY KEY,
    userid            VARCHAR(...),
    devicetoken       VARCHAR(...),
    facilityid        VARCHAR(...),
    userroles         VARCHAR(...),
    tenantid          VARCHAR(...),
    createdby         VARCHAR(...),
    createdtime       BIGINT,
    lastmodifiedby    VARCHAR(...),
    lastmodifiedtime  BIGINT
)
```

The exact column sizes vary by migration version.

## Business Flow

1. **On login or app start**, the mobile app registers its device token, linked to the user, facility, and roles. One token can be registered against several facilities.
2. **During the campaign**, an upstream service decides which facility or user needs an alert and publishes a push-notification event on Kafka.
3. **The service resolves the audience** through explicit device tokens, user UUIDs, or facility and role lookups in its own store.
4. **The message is sent to FCM**, which delivers the notification to each device. Phones that have uninstalled the app are cleaned out when FCM returns an `UNREGISTERED` token error.
5. When FCM is disabled, the message is **logged to the console** so flows can still be tested without live delivery.

## API Details

Context path: `/egov-notification-push`

Base paths:

* `/device-token/v1` — manage the phone-to-user registry
* `/push/v1` — send a push on demand

From a client's point of view:

* device-token APIs accept write requests, then persist asynchronously through Kafka
* `POST /push/v1/_send` resolves recipients and sends immediately
* the Kafka consumer path is still the main delivery path for campaign notifications

### Endpoints

<table><thead><tr><th width="281.140625">Endpoint</th><th>Purpose</th></tr></thead><tbody><tr><td><code>POST /device-token/v1/_register</code></td><td>Register a phone's FCM token for a user (with facility ids and roles). Persisted async via Kafka.</td></tr><tr><td><code>POST /device-token/v1/_unregister</code></td><td>Remove a token everywhere for a user (e.g. on logout), regardless of facility.</td></tr><tr><td><code>POST /device-token/v1/_delete</code></td><td>Delete a token, optionally scoped to specific facilities.</td></tr><tr><td><code>POST /device-token/v1/_search</code></td><td>Fetch the latest device token(s) for a set of user ids.</td></tr><tr><td><code>POST /push/v1/_send</code></td><td>Send a push <strong>on demand</strong>: resolves user UUIDs / device tokens, fires to FCM, returns how many devices were targeted.</td></tr></tbody></table>

**Kafka entry point (async, the main path).** A notification event lands on the configured push topic (default `egov.core.notification.push`, tenant-prefixed in central-instance mode) and is consumed by `PushNotificationListener`, which resolves the audience and sends to FCM. Device-token writes are published to `save-push-device-token-health` / `delete-…` / `unregister-…` for **egov-persister** to write to Postgres.

{% hint style="info" %}
No published Swagger contract exists for this service yet.
{% endhint %}

#### Kafka Topics

<table><thead><tr><th width="264.5546875">Topic</th><th width="100.734375">Dir</th><th>Purpose</th></tr></thead><tbody><tr><td><code>egov.core.notification.push</code></td><td>in</td><td>Inbound push-notification requests (central-instance: <code>{tenantId}-</code> prefixed pattern)</td></tr><tr><td><code>save-push-device-token-health</code></td><td>out</td><td>Persist device-token registration</td></tr><tr><td><code>delete-push-device-token-health</code></td><td>out</td><td>Persist device-token deletion</td></tr><tr><td><code>unregister-push-device-token-health</code></td><td>out</td><td>Persist device-token unregistration</td></tr></tbody></table>

## Process Flow

<figure><img src="https://2077406040-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FI0KFbSBTySIQ7TCOKnJF%2Fuploads%2FPdH5ykSxmGamPS2mvLVC%2Fimage.png?alt=media&amp;token=e76cc184-1e8e-40cb-872e-7cabd191535c" alt=""><figcaption></figcaption></figure>

Use this reading order:

1. **Register device tokens.** The mobile app writes token updates through the device-token APIs.
2. **Persist the registry.** The service emits Kafka events and **egov-persister** writes `eg_push_device_tokens`.
3. **Trigger a notification.** An upstream service publishes to Kafka, or a caller uses `POST /push/v1/_send`.
4. **Resolve the audience.** The service maps users, facilities, roles, or raw tokens to target devices.
5. **Send through FCM.** FCM handles actual delivery, and stale tokens are cleaned up when possible.

Registration and delivery are two separate flows.

The service reads the device-token registry directly from Postgres, but **writes** it through Kafka + egov-persister.

Delivery itself is offloaded to FCM.

### Register token flow

1. The mobile app calls `_register`, `_delete`, or `_unregister`.
2. The service validates the request and emits the matching Kafka event.
3. **egov-persister** writes the change into `eg_push_device_tokens`.
4. Later searches read the latest token state from Postgres.

### Kafka push delivery flow

1. An upstream service publishes a notification event on the inbound push topic.
2. `PushNotificationListener` consumes the event.
3. The service resolves recipients from explicit device tokens, user UUIDs, or facility and role filters.
4. The service batches tokens when needed and sends the payload to FCM.
5. Tokens reported as `UNREGISTERED` are removed from the registry.

### On-demand send flow

1. A caller sends `POST /push/v1/_send`.
2. The service resolves device tokens from the request body.
3. The service is sent directly to FCM.
4. The response returns the number of targeted devices.

{% hint style="info" %}
No official LLD sequence diagram is published for this service yet; the flow above reflects the current code.
{% endhint %}

## Failure & Retry Handling

* **Consumer is fail-soft.** `PushNotificationListener` wraps processing in a `try/catch` and logs errors. A bad message does not crash the consumer.
* **No automatic retry on consumer failure.** Failed push events are not retried automatically. Check service logs and the configured tracer error topic `notification-push-deadletter`.
* **No audience = no-op.** If no device tokens resolve (e.g. no phones registered for that facility/role), the send is skipped with a warning rather than erroring.
* **Stale-token cleanup.** When FCM reports a token as `UNREGISTERED` (app uninstalled), that token is deleted from the registry so it isn't tried again — keeps the store self-healing.
* **Multicast is batched.** Large audiences are split into FCM batches (default 500); a per-token failure inside a batch is logged and does not stop the rest of the batch.
* **FCM off → console fallback.** With `fcm.enabled=false` the `ConsolePushService` simply logs the notification; nothing is delivered, but flows still complete. With FCM on but credentials missing/blank, the app **fails fast at startup** (Firebase init throws).
* **Async registry writes.** `_register/_delete/_unregister` return `200` before the row is persisted. If the persister config for these topics is missing/stale in an environment, tokens are accepted but never stored — a classic "it worked in QA" trap.

{% hint style="info" %}

### HCM v2.1: Notification Service Consolidation & Push Enhancements

* **Standalone, buildable service**: The full service (Java source, `pom.xml`, `Dockerfile`, `start.sh`, tests, DB migrations) is now merged into this repo. Previously this folder held only resources and was deployed from elsewhere — that no longer applies.
* **Device-token registry**: Phones can register, unregister, delete, and search their FCM tokens via `/device-token/v1/*`. Tokens are persisted to Postgres (`eg_push_device_tokens`) through Kafka and egov-persister.
* **FCM push delivery with console fallback**: Delivery goes through Firebase (single or batched multicast). When FCM is disabled, notifications are logged to console instead, so non-prod environments can test the flow without sending real pushes.
* **Facility- and role-based targeting**: Notifications can target a facility and optionally a list of recipient roles (e.g., `WAREHOUSE_MANAGER`, `DISTRIBUTOR`). The listener resolves matching device tokens automatically. Supports one token across multiple facilities and role-based filtering.
* **On-demand send API**: `/push/v1/_send` allows immediate push by user UUIDs and/or raw device tokens, returning the count of devices targeted.
* **Multi-tenant / central-instance support**: In central-instance mode, topics are tenant-prefixed and the DB schema is derived from the tenant ID.
  {% endhint %}

## Known Risks & Limitations

* **No retry / dead-letter replay on the consumer.** Failed push events are logged and dropped; there is no built-in re-processing, so a transient FCM outage means those notifications are simply missed.
* **Delivery is best-effort.** FCM accepting a message does not guarantee the phone shows it; this service has no read-receipt or delivery-confirmation back-channel.
* **Hard dependency on the FCM service-account credentials.** With FCM enabled, a missing or malformed credential value crashes the service at startup.
* **Registry writes depend on external persister config.** If the device-token persister mappings aren't deployed in an environment, registrations silently never persist.
* **Role filtering is substring-based.** Roles are stored as a comma-separated string and matched with `LIKE`, so role codes that are substrings of one another could over-match — worth a QA check if role names overlap.
* **No Swagger / formal API contract** is published yet; integrators work from this README and the controllers.


---

# 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/health/design/architecture/low-level-design/services/platform-services/egov-notification-push.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.
