> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sublay.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Notification Preferences & Mute

> Automatic push fan-out for in-app events, per-user type toggles, per-conversation mute, and developer-authored copy templates.

Beyond the manual [`push.send()`](/v8/push-notifications) call, Sublay can **automatically** push in-app events (comments, mentions, follows, chat messages, …) to a user's registered devices. This page covers the surfaces that control that automatic delivery:

* **Per-user preferences** — each user can disable push for specific event types.
* **Per-conversation mute** — a user can silence a single conversation's push for a duration or forever.
* **Per-event copy templates & project-level toggles** — the developer authors the push title/body per event type and can disable an event project-wide, from the dashboard.

<Note>
  **These are per-event-type controls for one signed-in user, and they are not the same thing as turning push off for an account on a device.** A user with no disabled types still receives nothing on a device where their account has been silenced with [`useAccountPushToggle`](/v8/hooks/push/use-account-push-toggle) — that decides whether the account is bound to the device at all, while everything on this page decides which event types a bound account receives. The two compose, and neither overwrites the other.
</Note>

<Note>
  **Requires the `push` bundle.** Automatic push and the preference table live in the `push` bundle. Install it from your project's **Database** page in the [dashboard](https://dash.sublay.io). Chat-message push additionally requires the `chat` bundle; app-notification push requires the `notifications` bundle. See [Bundles](/v8/bundles).
</Note>

## The event palette

Every automatic push is keyed by an **event type**. These are the server's exact type names — the full app-notification set plus the chat `message` event (which is push-only and writes no in-app notification):

`entity-comment`, `comment-reply`, `entity-mention`, `comment-mention`, `entity-reaction`, `comment-reaction`, `entity-reaction-milestone-specific`, `entity-reaction-milestone-total`, `comment-reaction-milestone-specific`, `comment-reaction-milestone-total`, `new-follow`, `connection-request`, `connection-accepted`, `space-membership-approved`, `event-invite`, `event-updated`, `event-cancelled`, `reputation-grant`, `message`.

The same names are used everywhere: a user's `disabledTypes`, the project-level per-event toggle, and the template map all key on this exact list. The SDKs export the list as `PUSH_EVENT_TYPES` (`@sublay/core`, `@sublay/node`, `@sublay/js`).

## Preference & mute model

### Per-user preferences (`disabledTypes`)

Each user has at most one preference row, holding the set of event types they've **opted out** of for push. The model is:

* **Absence of a row = all-on.** A user with no preference row receives push for every enabled event type. Reading preferences for such a user returns `{ "disabledTypes": [] }`.
* **Updating replaces the set.** The update endpoint upserts the whole `disabledTypes` array — send the complete set you want stored, not a delta.
* **Only valid type names are accepted.** Unknown names are rejected server-side.

Disabling a type only suppresses **push** — the in-app [App Notification](/v8/sdk/app-notifications/overview) is still written, so the bell/inbox is unaffected.

### Per-conversation mute (`mutedUntil` / `mutedForever`)

A user can mute a single conversation for a fixed duration or forever. Mute suppresses the `message` push for that conversation only; the message is still delivered, socket-emitted, and available in the conversation as normal.

The client sends a **duration choice**, never a raw timestamp — the server resolves the window from its own clock so client skew can't distort it:

| Choice | Meaning |
| - | - |
| `8h` | Muted for 8 hours from now |
| `24h` | Muted for 24 hours from now |
| `1w` | Muted for 1 week from now |
| `forever` | Muted indefinitely |
| `null` | Clear the mute (unmute) |

Timed mutes expire lazily — once the window passes, push resumes with no user action.

<Warning>
  **"Forever" is an explicit signal, not a magic date.** Internally the server stores "forever" as a far-future sentinel timestamp, but that value **never** reaches clients. At the API boundary the viewer's own member row carries `mutedForever: true` with `mutedUntil: null`. Read the boolean — do **not** string-match a far-future date. The SDKs mirror this exactly.
</Warning>

The viewer's own member row (`currentMember`) is serialized as:

| State | `mutedForever` | `mutedUntil` |
| - | - | - |
| Not muted | `false` | `null` |
| Timed mute | `false` | real ISO timestamp |
| Muted forever | `true` | `null` |

<Info>
  **Mute state is personal.** A member's `mutedUntil` / `mutedForever` is present **only** on that member's own row. Endpoints that return *other* members' rows (`listMembers`, `changeMemberRole`, `addMember`, the `member:joined` socket broadcast) omit both keys entirely — a user's mute state is never exposed to other participants.
</Info>

## Gating precedence

When an event fires, whether a push is dispatched is decided in this order:

1. **Project-level event disable** (dashboard toggle) — if the developer disabled the event, no one is pushed.
2. **Conversation mute** (chat `message` only) — muted members are dropped.
3. **Per-user type toggle** — users who disabled the type are dropped.
4. **Send** to everyone remaining who has a registered device.

## Copy templates & project-level toggles (dashboard)

The developer authors the push **title/body copy** per event type and can **disable** an event project-wide, from **Settings → Push Notifications** in the [dashboard](https://dash.sublay.io), beside the provider configuration.

* Each event type has an editable template field and a project-level enable toggle.
* A template is a plain-text string with `{variableName}` placeholders. Substitution is plain-text only — no expressions, no markup execution. Unknown tokens render empty.
* If a project configures no template for an event, a shipped default is used. Template edits take effect on the next send.

### Template variable palette

Each event exposes a fixed set of variables its template may interpolate. Values come from the notification metadata (app-notification events) or the message context (`message`). A token that isn't in the event's list renders empty.

| Event type | Variables |
| - | - |
| `entity-comment` | `initiatorName`, `initiatorUsername`, `entityTitle`, `commentContent` |
| `comment-reply` | `initiatorName`, `initiatorUsername`, `entityTitle`, `replyContent` |
| `entity-mention` | `initiatorName`, `initiatorUsername`, `entityTitle` |
| `comment-mention` | `initiatorName`, `initiatorUsername`, `entityTitle`, `commentContent` |
| `entity-reaction` | `initiatorName`, `initiatorUsername`, `entityTitle`, `reactionType` |
| `comment-reaction` | `initiatorName`, `initiatorUsername`, `entityTitle`, `commentContent`, `reactionType` |
| `entity-reaction-milestone-specific` | `entityTitle`, `reactionType`, `milestoneCount` |
| `entity-reaction-milestone-total` | `entityTitle`, `milestoneCount` |
| `comment-reaction-milestone-specific` | `entityTitle`, `commentContent`, `reactionType`, `milestoneCount` |
| `comment-reaction-milestone-total` | `entityTitle`, `commentContent`, `milestoneCount` |
| `new-follow` | `initiatorName`, `initiatorUsername` |
| `connection-request` | `initiatorName`, `initiatorUsername` |
| `connection-accepted` | `initiatorName`, `initiatorUsername` |
| `space-membership-approved` | `spaceName` |
| `event-invite` | `initiatorName`, `initiatorUsername`, `eventTitle` |
| `event-updated` | `initiatorName`, `initiatorUsername`, `eventTitle` |
| `event-cancelled` | `initiatorName`, `initiatorUsername`, `eventTitle` |
| `reputation-grant` | `amount`, `granterName`, `granterUsername`, `note` |
| `message` | `senderName`, `senderUsername`, `conversationTitle`, `messageContent` |

Example template for `entity-comment`:

```
{initiatorName} commented on {entityTitle}: {commentContent}
```

## Privacy

Muted-conversation content and disabled-type content is **simply not dispatched** — when a mute or a preference suppresses a push, the notification's title/body (which for chat/comments contains a content snippet) is never sent to APNs, FCM, or Web Push at all. Suppression happens before any provider call, so muted/opted-out content never leaves Sublay's infrastructure to a third-party push provider.

## Building a settings UI

The SDKs expose the read/update primitives so you can build a notification-settings screen and per-conversation mute control:

<CardGroup cols={2}>
  <Card title="useNotificationPreferences" icon="cube" href="/v8/hooks/push/use-notification-preferences">
    Read + update the current user's disabled types (React / RN / Expo)
  </Card>

  <Card title="useMuteConversation" icon="cube" href="/v8/hooks/chat/conversations/use-mute-conversation">
    Set / clear the current user's conversation mute
  </Card>

  <Card title="Node SDK — Push Notifications" icon="node-js" href="/v8/node-sdk/push-notifications">
    `push.getNotificationPreferences` / `updateNotificationPreferences`, `chat.muteConversation`
  </Card>

  <Card title="JS SDK — Push Notifications" icon="js" href="/v8/js-sdk/push-notifications">
    `push.getNotificationPreferences` / `updateNotificationPreferences`, `chat.muteConversation`
  </Card>
</CardGroup>

### API endpoints

<CardGroup cols={2}>
  <Card title="Mute Conversation" href="/v8/api-reference/chat/conversations/mute-conversation">
    `POST /chat/conversations/:conversationId/mute`
  </Card>

  <Card title="Get Notification Preferences" href="/v8/api-reference/push-notifications/get-preferences">
    `GET /push-notifications/preferences`
  </Card>

  <Card title="Update Notification Preferences" href="/v8/api-reference/push-notifications/update-preferences">
    `PUT /push-notifications/preferences`
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.