Configure notification preferences in Novu at the workflow and subscriber level. Set default channel preferences (email, SMS, push, chat, in-app), mark preferences as read-only or subscriber-editable, and manage subscriber-specific overrides. Use when setting up notification opt-in/opt-out, configuring per-channel delivery preferences, or building a preferences management UI.
85%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
Passed
No findings from the security scan
Novu has a two-level preference system:
Set default preferences when defining a workflow with @novu/framework:
import { workflow } from "@novu/framework";
const alertWorkflow = workflow("system-alert", execute, {
preferences: {
all: { enabled: true, readOnly: false },
channels: {
email: { enabled: true },
sms: { enabled: false },
push: { enabled: true },
chat: { enabled: false },
inApp: { enabled: true },
},
},
});Authoring workflows in code? See
framework-integrationfor the full Framework setup, Bridge Endpoint, step controls, and deployment.
| Channel | Description |
|---|---|
email | Email notifications |
sms | SMS text messages |
push | Mobile/web push notifications |
chat | Slack, Discord, Teams, etc. |
inApp | In-app Inbox notifications |
Set readOnly: true to hide a workflow's channels from the Preferences UI — subscribers can't toggle them on or off:
const criticalAlertWorkflow = workflow("critical-alert", execute, {
preferences: {
all: { enabled: true, readOnly: true }, // subscriber CANNOT disable
},
});readOnly vs critical — pick the right oneThese are different mechanisms with different guarantees. See design-workflow/references/severity-and-critical.md for the full matrix.
| Flag | What it does |
|---|---|
preferences.all.readOnly: true | UI only. Hides the workflow from the Preferences UI so subscribers can't toggle it. |
critical: true (workflow-level) | Runtime. Bypasses subscriber preferences, skips digest, runs without delays. |
If you need the notification to always be delivered (account suspended, security alert, password reset), set critical: true — readOnly: true alone won't override existing subscriber overrides at runtime.
const marketingWorkflow = workflow("weekly-newsletter", execute, {
preferences: {
all: { enabled: true, readOnly: false }, // subscriber CAN disable
channels: {
email: { enabled: true },
sms: { enabled: false }, // off by default, subscriber can enable
},
},
});Subscribers can override workflow defaults (unless readOnly: true).
import { Novu } from "@novu/api";
const novu = new Novu({
secretKey: process.env.NOVU_SECRET_KEY,
});
const preferences = await novu.subscribers.preferences.list({
subscriberId: "subscriber-123",
});await novu.subscribers.preferences.update(
{
workflowId: "weekly-newsletter",
channels: {
email: false, // opt out of email
inApp: true, // keep in-app
},
},
"subscriber-123"
);Update preferences across all workflows by omitting workflowId:
await novu.subscribers.preferences.update(
{
channels: {
sms: false, // disable SMS for all workflows
},
},
"subscriber-123"
);When Novu determines whether to deliver a notification:
The most specific preference wins. If a subscriber disables email for a specific workflow, that takes precedence even if their global email preference is enabled.
import { Inbox } from "@novu/react";
function App() {
return (
<Inbox
applicationIdentifier="YOUR_NOVU_APP_ID"
subscriberId="subscriber-123"
subscriberHash="HMAC_HASH"
>
{/* The Preferences panel is built into the Inbox */}
</Inbox>
);
}The <Inbox /> component includes a built-in Preferences panel accessible via the settings icon.
Use the <Preferences /> component independently:
import { Inbox, Preferences } from "@novu/react";
function PreferencesPage() {
return (
<Inbox
applicationIdentifier="YOUR_NOVU_APP_ID"
subscriberId="subscriber-123"
>
<Preferences />
</Inbox>
);
}preferences: {
all: { enabled: true, readOnly: true },
}Subscribers cannot opt out. Use for security alerts, payment notifications, legal notices.
preferences: {
all: { enabled: true, readOnly: false },
channels: {
email: { enabled: true },
sms: { enabled: false },
},
}Subscribers can toggle channels. SMS is off by default.
preferences: {
all: { enabled: false },
channels: {
inApp: { enabled: true },
},
}Only in-app is on. Subscribers can enable other channels if desired.
readOnly: true is per-workflow, not per-channel — you set readOnly on the all level. Individual channels inherit it.readOnly workflows — if the workflow is read-only, subscriber preferences are ignored.enabled: false in the workflow default means the channel is off — subscribers can still enable it (unless readOnly: true).novuhq/novu
811a64d
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.