# Push notifications

Providers with active Premium can send push notifications to their subscribers. All messages go through moderation in INCY, except when sent to your own device via [admin HWID](broken://pages/af2699ef3c92c4fe2842661e849a8e4caaa61a1d).

***

## Notification lifecycle

{% stepper %}
{% step %}

### Creation

The provider clicks “Send” in the panel → the record is added to the collection `pendingNotifications` collection with status `pending`.
{% endstep %}

{% step %}

### Moderation

The INCY bot shows the request to the admin; they click “Approve” or “Reject with comment.”
{% endstep %}

{% step %}

### Fan-out

If approved, the server selects devices by `targetSegment`, writes the metadata to their device documents (for desktop polling) and sends an FCM push to each device that has `fcmToken`.
{% endstep %}

{% step %}

### Statuses

The provider sees the status and counters in the panel `deliveredCount` / `failedCount`.
{% endstep %}
{% endstepper %}

### Record statuses

| Status      | Value                                                                     |
| ----------- | ------------------------------------------------------------------------- |
| `pending`   | Waiting for moderation                                                    |
| `approved`  | Approved by moderator (or auto-approved); FCM distribution completed      |
| `rejected`  | Rejected by moderator. `moderatorComment` contains the reason             |
| `cancelled` | The provider canceled before approval. The distribution was not performed |
| `failed`    | Approved, but fan-out failed (no verified domains / FCM error)            |

***

## Sending: notification fields

| Field           | Type      | Required | Description                                               |
| --------------- | --------- | :------: | --------------------------------------------------------- |
| `title`         | `string`  |    yes   | Title, ≤ 100 characters                                   |
| `body`          | `string`  |    yes   | Text, ≤ 500 characters                                    |
| `image`         | `string?` |    no    | Large image URL (rendered in the expanded push)           |
| `url`           | `string?` |    no    | URL for the notification button / tap                     |
| `urlButtonName` | `string?` |    no    | Button label. Defaults to “Open”                          |
| `forceTimer`    | `number?` |    no    | Enterprise-only: show a modal dialog for N seconds (1–10) |
| `targetSegment` | `object`  |    yes   | Targeting parameters (below)                              |

### Targeting (`targetSegment`)

| Field        | Type        | Description                                                                         |
| ------------ | ----------- | ----------------------------------------------------------------------------------- |
| `platform`   | `string`    | `all` \| `ios` \| `android` \| `linux` \| `windows` \| `macos`                      |
| `region`     | `string[]?` | Device locale (for example `["ru", "by"]`). Comparison is case-insensitive          |
| `domain`     | `string?`   | A specific subscription domain of the provider (if the provider has several)        |
| `activeDays` | `number?`   | Only devices that appear to have been active in the last N days                     |
| `appVersion` | `string?`   | Only a specific app version (for example `"2.5.6"`)                                 |
| `hwid`       | `string?`   | A specific HWID. Includes [admin-HWID auto-approve](#auto-approve-через-admin-hwid) |

All filters are combined with logical **AND**. Empty / unspecified fields do not restrict delivery.

### Delivery

* **Android / iOS** receive push via FCM.
* **Desktop (Linux / Windows)** polls its own device document in Firestore and reads the fields `pendingNotificationTitle`, `pendingNotificationBody`, `pendingNotificationAt`, `pendingNotificationUrl`, `pendingNotificationUrlButtonName`, `pendingNotificationForceTimer` — separately from FCM, since desktop has no FCM.

### Limitations

* Only for domains with `isVerified: true` from the provider.
* Only with active Premium (`isPremium: true` in Firestore).
* `forceTimer` > 0 works only on the Enterprise plan.

***

## Auto-approve via admin HWID

If `targetSegment.hwid` matches one of `adminHwids` of any verified provider domain — the record is created immediately with status `approved`, moderation is skipped, FCM is sent immediately.

Details: [admin-hwids.md](broken://pages/af2699ef3c92c4fe2842661e849a8e4caaa61a1d).

This is the only way the provider can send a notification bypassing moderation.

***

## Canceling a delivery

While the status is `pending`, the provider can cancel the notification in the panel. As a result:

* The status changes to `cancelled`.
* If the moderator decides “Approve” at that moment — the bot will receive the response “The provider canceled the delivery” and FCM will not be executed.

After switching to any terminal status (`approved` / `rejected` / `cancelled` / `failed`) — the operation is irreversible.

***

## Audit

Each record in `pendingNotifications` stores:

| Field               | Description                                                  |
| ------------------- | ------------------------------------------------------------ |
| `providerId`        | provider UID                                                 |
| `providerEmail`     | provider email at the time of sending                        |
| `createdAt`         | When the provider clicked “Send”                             |
| `moderatedAt`       | When the moderator decided / the system auto-approved        |
| `moderatorComment`  | Rejection text (for `rejected`)                              |
| `autoApproved`      | `true` if it bypassed moderation                             |
| `autoApproveReason` | Reason, for example `"adminHwid"`                            |
| `cancelledAt`       | For `cancelled` — when the provider canceled                 |
| `cancelledBy`       | Cancellation source (currently only `"provider"`)            |
| `deliveredCount`    | Number of successfully sent pushes + desktop-polling devices |
| `failedCount`       | FCM errors                                                   |


---

# Agent Instructions: 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:

```
GET https://incy.gitbook.io/docs/docs-en/premium-api/push-notifications.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
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.
