novu-design-workflow

Design notification workflows the Novu way — choose channels, set severity, decide when a workflow is critical, configure digests, and route based on subscriber state. Applies to BOTH dashboard-authored and code-first (`@novu/framework`) workflows. Use when planning a new workflow, deciding which channels to include, picking severity, configuring digest behavior, or matching a use case (order confirmation, payment failed, account suspended, comment, trial expiring, password reset, webhook fan-out, fetch-then-notify) to a proven template.

novuhq/skills419 installsMITSynced Aug 26

Works with

Claude CodeCursorCodex CLIGitHub CopilotGemini CLI
---
name: novu-design-workflow
description: Design notification workflows the Novu way — choose channels, set severity, decide when a workflow is critical, configure digests, and route based on subscriber state. Applies to BOTH dashboard-authored and code-first (`@novu/framework`) workflows. Use when planning a new workflow, deciding which channels to include, picking severity, configuring digest behavior, or matching a use case (order confirmation, payment failed, account suspended, comment, trial expiring, password reset, webhook fan-out, fetch-then-notify) to a proven template.
license: MIT
---

# Design Workflow

Design rules for any Novu workflow — independent of whether you author it in the **Dashboard** (no-code) or in **code** with [`@novu/framework`](../framework-integration). The decisions here (channels, severity, critical, digest, conditions) are the same on both surfaces; only the syntax differs.

> Authoring **in code**? Pair this skill with [`framework-integration/`](../framework-integration) for `workflow(...)`, `step.*`, `controlSchema`, and Bridge Endpoint setup.
> Authoring **in the Dashboard or via the Novu MCP**? After designing here, fill in step content (subject, body, `editorType`, headers, conditions) using [`dashboard-workflows/`](../dashboard-workflows).

## When to use this skill

Use it whenever you need to **decide what a workflow should look like**:

- "Design an order-confirmation workflow"
- "Which channels should I send for a payment failure?"
- "Make this notification critical"
- "Should this be digested?"
- "Add a fallback for offline subscribers"
- "What's the right template for X?"

Do **not** use it for: triggering an existing workflow ([`trigger-notification/`](../trigger-notification)), authoring code wrappers ([`framework-integration/`](../framework-integration)), or rendering severity in the UI ([`inbox-integration/`](../inbox-integration)).

## Severity & Critical

Two independent dials. Most workflows set **neither**.

| Dial       | Values                          | Default      | Effect                                                                          |
| ---------- | ------------------------------- | ------------ | ------------------------------------------------------------------------------- |
| `severity` | `LOW` / `MEDIUM` / `HIGH`       | unset        | Visual prioritization in the Inbox (color, glow); informs digest skip rules.    |
| `critical` | `true` / `false`                | `false`      | Bypass subscriber preferences, skip digest, no delays, all available channels.  |

Rules of thumb:

- Leave `severity` unset for most workflows. Only set it when visual prioritization is needed.
- `HIGH` = "deal with this today" (payment failed, trial expiring tomorrow).
- `critical: true` = "deliver regardless of preferences" (account suspended, security alert, password reset).
- `critical: true` ⇒ digest is automatically skipped and channels deliver immediately.

See [`references/severity-and-critical.md`](./references/severity-and-critical.md) for the full behavior matrix and the `readOnly` vs `critical` distinction.

## Channel Selection

### If the user specified channels

Use **only** those channels. Do not add fallbacks. Do not add extras. If the requested channel isn't configured in the organization, add it anyway because the user explicitly asked for it.

> "Send a push notification when the order ships" → one `push` step. Nothing else.

### If the user did NOT specify channels

Pick from channels **configured in the organization**, in this priority order, up to **3 channels**:

```
In-App  >  Email  >  Chat  >  Push  >  SMS
```

| Channel | Use it for                                                                                          | Skip it when                                                |
| ------- | --------------------------------------------------------------------------------------------------- | ----------------------------------------------------------- |
| In-App  | Default for in-product content. Always include if the user is in your product.                      | The recipient can't see it (password reset, OTP, pre-signup) |
| Email   | Receipts, documentation, async communication. Default fallback after In-App.                        | Pure conversational pings inside the product                 |
| Chat    | If configured AND `severity >= MEDIUM`. Slack/Teams for ops & internal flows.                       | Marketing or low-severity nudges                             |
| Push    | Fallback when subscriber is **offline** but needs immediate awareness.                              | Subscriber is online (use In-App instead)                    |
| SMS     | Last resort. Only when no other channel works (true emergencies, OTP, regulatory).                  | Anything that fits in Email or Push                          |

See [`references/channel-selection.md`](./references/channel-selection.md) for the full decision tree.

## Digest Defaults

When you add a digest step, default to:

- `type: "regular"`
- look-back window: **5 minutes**
- digest time: **1 hour**
- key: `subscriberId` (and `+threadId` for conversational flows)

**Skip the digest** when:

- `severity: HIGH`, or
- `critical: true`

See [`references/digest-defaults.md`](./references/digest-defaults.md) for digest key composition and conversational examples.

## User-State Logic

Adapt routing based on whether the subscriber is online:

| State    | Behavior                                                                                                |
| -------- | ------------------------------------------------------------------------------------------------------- |
| Online   | Send In-App immediately. Skip Push. Delay Email/Chat based on severity.                                 |
| Offline  | Use Push or Chat to get attention.                                                                      |

Default delays:

- **B2B** apps → next work hour
- **B2C** apps → ~30 minutes

The condition for "subscriber offline" is the same on both surfaces — see [`references/step-conditions.md`](./references/step-conditions.md).

## Workflow Templates

Match the use case to a template and copy its shape. Each template specifies severity, critical, actionable, and interaction type, plus the step ordering.

| # | Use case                       | Severity | Critical | Notes                                            |
| - | ------------------------------ | -------- | -------- | ------------------------------------------------ |
| 1 | Order Confirmation             | none     | false    | Digested, In-App + Email + Push (offline only)   |
| 2 | Comment on Your Post           | none     | false    | Digested by `subscriberId + threadId`            |
| 3 | Payment Failed                 | HIGH     | false    | In-App + Chat + Email + Push (offline)           |
| 4 | Account Suspended              | HIGH     | true     | All channels, no preferences, no digest          |
| 5 | Forgot Password                | none     | true     | Email + SMS only, no In-App                      |
| 6 | Trial Expiring Tomorrow        | HIGH     | false    | In-App + Chat + Email + Push (offline)           |
| 7 | Explicit Channel Request       | n/a      | n/a      | Use only the channels the user specified         |
| 8 | Webhook / External API Call    | varies   | varies   | Add `step.http` after channel steps              |
| 9 | Fetch Data then Notify         | varies   | varies   | `step.http` first; declare `responseBodySchema`  |

Full ASCII flows + per-template metadata in [`references/workflow-templates.md`](./references/workflow-templates.md).

## Step Conditions

Conditions decide whether a step runs. Use them for "send only if subscriber is offline", "send email only if In-App wasn't seen", and similar fallbacks.

- **Dashboard** authors write [JSON-Logic](https://jsonlogic.com): `{ "==": [{ "var": "subscriber.isOnline" }, "false"] }`
- **Framework** authors pass a `skip: () => boolean` callback to the step.

The semantics are identical. See [`references/step-conditions.md`](./references/step-conditions.md) for the canonical snippets and the variables available in each scope.

## Common Pitfalls

1. **Don't set severity by default** — leave it unset unless you actually need visual prioritization.
2. **`critical: true` is not the same as `readOnly: true`** — `readOnly` only hides the workflow from the Preferences UI; `critical` bypasses preferences and digests at runtime. See [`references/severity-and-critical.md`](./references/severity-and-critical.md).
3. **Don't add fallbacks when the user named the channels** — explicit channel requests are exact.
4. **Cap the channel count at 3** when the user didn't specify channels. More channels = more annoyance, not more reach.
5. **Don't combine digest with `critical: true`** — critical workflows must deliver immediately. The digest step is auto-skipped.
6. **Digest key matters for conversational flows** — without `+threadId`, a comment on Post A and a comment on Post B end up in the same digest.
7. **Push only when offline** — sending push to an online user duplicates the In-App alert.
8. **HTTP step needs `responseBodySchema`** — without it, downstream steps can't read response properties via `{{ steps.<id>.<prop> }}`.

## References

- [Channel Selection](./references/channel-selection.md) — full decision tree and per-channel guidance
- [Severity & Critical](./references/severity-and-critical.md) — behavior matrix, preference & digest interactions, `readOnly` vs `critical`
- [Digest Defaults](./references/digest-defaults.md) — windows, keys, conversational digest patterns
- [Step Conditions](./references/step-conditions.md) — JSON-Logic snippets and Framework `skip` equivalents
- [Workflow Templates](./references/workflow-templates.md) — the 9 reference flows with severity/critical/interaction tables

## See Also

- [`dashboard-workflows/`](../dashboard-workflows) — author step content (subject, body, `editorType`, headers, conditions) for Dashboard or Novu MCP workflows
- [`framework-integration/`](../framework-integration) — implement these designs in code (`workflow()`, `step.*`, `controlSchema`, Bridge)
- [`manage-preferences/`](../manage-preferences) — how `critical` interacts with subscriber-level preferences
- [`inbox-integration/`](../inbox-integration) — how severity surfaces visually in the Inbox
- [`trigger-notification/`](../trigger-notification) — invoking a workflow once it's designed

More API Design skills

← All API Design skills

Check your AI visibility

One URL in, a 0–100 score and the exact fixes out.

RUN THE CHECK

Browse all the tools

15 tools across six categories
13 of them never send your data anywhere

Free · No signup · No trial clock

SEE THE DIRECTORY