# Notiflows Documentation The complete notification infrastructure for modern SaaS. Build cross-channel notifications that drive engagement with in-app, push, email, SMS, web push, chat, and more. # Welcome to Notiflows > The complete notification infrastructure for modern applications Source: https://notiflows.com/docs --- title: Welcome to Notiflows description: The complete notification infrastructure for modern applications --- # Welcome to Notiflows Notiflows is a comprehensive notification infrastructure that helps you deliver the right message to the right user at the right time, across any channel. ## Get Started ## Explore - **[Concepts](/docs/concepts/notiflows)** - Learn the core concepts behind Notiflows - **[Channels & Providers](/docs/channels-providers/overview)** - Configure your notification channels - **[SDKs](/docs/sdks/server-side/node)** - Integrate with your application - **[Notiflows for AI](/docs/ai)** - Manage notiflows as code, and give your AI agents typed tools to trigger them --- # Notiflows for AI > Notiflows is AI-native — manage notiflows as code and let your own AI agents inspect, create, and trigger them. Your AI, your tokens. Source: https://notiflows.com/docs/ai --- title: Notiflows for AI description: Notiflows is AI-native — manage notiflows as code and let your own AI agents inspect, create, and trigger them. Your AI, your tokens. --- Notiflows is built so AI tools and agents can work with it natively. Every notiflow is an explicit, declarative definition an AI coding assistant can read, generate, review, and ship — and that your agents can trigger at runtime as a typed tool. ## Your AI, your tokens The guiding principle behind every AI surface: **your AI, your tokens.** Each surface runs inside *your* AI tool or agent, on *your* model, paid for with *your* keys. Notiflows hosts no LLM and never runs a model on your behalf. The MCP server, agent toolkit, and skills are all thin clients over the [Management API](/docs/api) — they give your assistant the context and the tools to manage and trigger notiflows, but the intelligence (and the bill for it) stays with you. The only credential Notiflows needs is your account token, which authorizes API calls and is never exposed to the model. This keeps the boundary clean: Notiflows is the notification platform; your AI is your AI. Every surface authenticates with a single [account token](/docs/cli/account-tokens) (prefix `nf_at_`) — create one in the dashboard and supply it to whichever surface you use. ## The surfaces A hosted Model Context Protocol server that exposes the Management API as tools, so you can manage and trigger notiflows conversationally in Claude Code, Cursor, or Claude Desktop. @notiflows/agent-toolkit — turns your published notiflows into typed tools for your own AI agents (Vercel AI SDK, OpenAI, LangChain). Installable, progressive-disclosure skills that teach any compatible assistant the Notiflows schemas and CLI. @notiflows/cli — pull, edit, and push notiflows as local files your assistant can read and write directly. Machine-readable docs — llms.txt, llms-full.txt, and raw-markdown doc routes for grounding an assistant. ## How they fit together The [Management API](/docs/api) is the source of truth for creating notiflows. The [CLI](/docs/cli) and the [MCP server](/docs/ai/mcp) are two interfaces over it — files-on-disk and conversational tools, respectively — sharing the same auth (an account token) and the same flat-steps notiflow shape. The [agent toolkit](/docs/ai/agent-toolkit) is for the runtime side: giving *your* agents the ability to trigger notifications as function calls. And [skills](/docs/ai/skills) plus [llms.txt](/docs/ai/llms-txt) ground any assistant in the real schemas so it produces valid notiflows on the first try. --- # Agent Toolkit > The Notiflows agent toolkit (@notiflows/agent-toolkit) turns your published notiflows into typed, callable tools for your own AI agents — Vercel AI SDK, OpenAI function calling, and LangChain. Source: https://notiflows.com/docs/ai/agent-toolkit --- title: Agent Toolkit description: The Notiflows agent toolkit (@notiflows/agent-toolkit) turns your published notiflows into typed, callable tools for your own AI agents — Vercel AI SDK, OpenAI function calling, and LangChain. --- The agent toolkit (`@notiflows/agent-toolkit`) exposes your **notiflows as typed, callable tools** for AI agents. Where the [MCP server](/docs/ai/mcp) lets an editor *manage* notiflows, the toolkit lets your own agents *use* Notiflows at runtime — sending the right notification, to the right people, with a schema-safe payload, where every send is attributed and auditable. Each **published** notiflow in your project becomes a typed tool (e.g. `trigger_welcome_series`), alongside a generic `trigger_notiflow` tool that takes the handle as an argument. The toolkit runs inside your agent, on your model — the account token authorizes API calls server-side and is never exposed to the LLM. ```bash npm i @notiflows/agent-toolkit ``` ## Create the toolkit ```ts import { createNotiflowsToolkit } from "@notiflows/agent-toolkit"; const toolkit = createNotiflowsToolkit({ accountToken: process.env.NOTIFLOWS_TOKEN!, // nf_at_... project: "acme", }); ``` | Option | Required | Description | |---|---|---| | `accountToken` | yes | Notiflows [account token](/docs/cli/account-tokens) (`nf_at_...`). | | `project` | yes | Project slug. | | `baseUrl` | no | Management API base (defaults to `https://api.notiflows.com/management/v1`). | | `notiflows` | no | An allowlist of handles, or a `(notiflow) => boolean` predicate. Omit to expose all published notiflows. | ## Tool arguments Every trigger tool takes the same arguments — provide **`recipients`** or a **`topic`**: | Argument | Description | |---|---| | `recipients` | Users to notify (provide this *or* `topic`). Each recipient is `external_id` (required) plus optional `email`, `phone`, `first_name`, `last_name` — used to auto-create the user if they don't exist yet. | | `topic` | Topic name — notifies all of its subscribers (provide this *or* `recipients`). | | `data` | Template variables, available in the notification as `data.*`. | | `actor` | The user who caused the action (`{ external_id }`), available as `actor.*`. | The generic `trigger_notiflow` tool also takes `handle`, plus an optional `draft` (`"published"` — the default — or `"draft"` to test the current unpublished version). ## Vercel AI SDK ```ts import { generateText } from "ai"; import { openai } from "@ai-sdk/openai"; import { createNotiflowsToolkit } from "@notiflows/agent-toolkit"; import { toAISDKTools } from "@notiflows/agent-toolkit/ai-sdk"; const toolkit = createNotiflowsToolkit({ accountToken: process.env.NOTIFLOWS_TOKEN!, project: "acme", }); const tools = await toAISDKTools(await toolkit.getTools()); await generateText({ model: openai("gpt-4o"), prompt: "Email jane@acme.com the welcome series; her id is user_42.", tools, // trigger_welcome_series, trigger_order_shipped, ... + trigger_notiflow }); ``` ## OpenAI function calling ```ts import OpenAI from "openai"; import { createNotiflowsToolkit, toOpenAITools } from "@notiflows/agent-toolkit"; const toolkit = createNotiflowsToolkit({ accountToken: process.env.NOTIFLOWS_TOKEN!, project: "acme" }); const { tools, execute } = toOpenAITools(await toolkit.getTools()); const completion = await new OpenAI().chat.completions.create({ model: "gpt-4o", messages, tools, }); for (const call of completion.choices[0].message.tool_calls ?? []) { const result = await execute(call.function); // { notiflow_run_id } } ``` ## LangChain ```ts import { toLangChainTools } from "@notiflows/agent-toolkit/langchain"; const tools = await toLangChainTools(await toolkit.getTools()); // DynamicStructuredTool[] ``` ## Framework-agnostic `getTools()` returns plain tool descriptors you can wire into any framework: ```ts const tools = await toolkit.getTools(); // [{ name, description, inputSchema, execute }] await tools[0].execute({ recipients: [{ external_id: "user_42" }], data: { plan: "pro" }, }); ``` ## Human-in-the-loop `requireHumanInput` triggers an approval notiflow (typically an in-app channel) before an agent proceeds, then you wait for the human's response out-of-band: ```ts const { notiflow_run_id } = await toolkit.requireHumanInput({ notiflow: "agent-approval", recipients: [{ external_id: "ops_admin" }], message: "Agent wants to issue a $200 refund. Approve?", data: { amount: 200 }, }); // Wait for the response via webhook/poll on the delivery, then continue. ``` ## Why it's safe to hand to an agent - **Scoped** — the toolkit only exposes notiflows you allow (the `notiflows` allowlist or predicate), and only *published* ones. - **Schema-safe** — each tool advertises a JSON Schema; recipients, topic, and data are validated by the API before anything sends. - **Auditable** — every trigger goes through the Management API with your account token, so it shows up in the dashboard's **"Triggered by"** as the token. - **Server-side only** — the account token is never exposed to the model. ## API surface - `createNotiflowsToolkit(config)` → `NotiflowsToolkit` - `toolkit.getTools({ perNotiflow?, includeGeneric? })` → `NotiflowTool[]` - `toolkit.genericTool()` → the `trigger_notiflow` tool (handle as an argument; supports `draft`) - `toolkit.listNotiflows()` → the published notiflows exposed as tools - `toolkit.requireHumanInput(opts)` → trigger an approval notiflow - Adapters: `toAISDKTools`, `toOpenAITools`, `toLangChainTools` The peer dependencies `ai`, `openai`, and `@langchain/core` are **optional** and loaded lazily — install only the adapter(s) you actually use. ## Related - [MCP server](/docs/ai/mcp) — manage notiflows conversationally in an editor. - [CLI](/docs/cli) — create and trigger notiflows from the terminal. - [Agent skills](/docs/ai/skills) — teach your assistant the notiflow schemas and CLI. - [llms.txt & docs for agents](/docs/ai/llms-txt) — machine-readable docs for grounding an assistant. - [Notiflows for AI](/docs/ai) — the AI surfaces overview. --- # llms.txt & docs for agents > Machine-readable Notiflows documentation for grounding AI assistants — llms.txt, llms-full.txt, and raw-markdown routes for individual doc pages. Source: https://notiflows.com/docs/ai/llms-txt --- title: llms.txt & docs for agents description: Machine-readable Notiflows documentation for grounding AI assistants — llms.txt, llms-full.txt, and raw-markdown routes for individual doc pages. --- Notiflows publishes its documentation in machine-readable forms so you can drop authoritative context into an AI assistant instead of relying on its training data. Everything here is generated from the same docs you're reading, so it never drifts. ## `/llms.txt` A concise, link-first index of the documentation, following the [llms.txt convention](https://llmstxt.org). It lists every doc page (grouped by section) with a one-line description and a link, so an assistant — or you — can quickly find the right page and fetch it on demand. ``` https://notiflows.com/llms.txt ``` Use it as the lightweight entry point: hand it to an assistant that can follow links, and it will pull the specific pages it needs rather than ingesting everything. ## `/llms-full.txt` The entire documentation concatenated into one plain-text file — every page's title, description, source URL, and full markdown body. Use it when you want to load the complete docs into a model's context in a single fetch (for a coding agent, a RAG index, or a one-shot prompt). ``` https://notiflows.com/llms-full.txt ``` The generated API reference is intentionally excluded from `llms-full.txt` because those pages are component tags rather than readable prose — point agents at the [API reference](/docs/api) directly for endpoint details. ## Raw markdown per page Every doc page is also available as raw markdown — just **append `.md` to any docs URL**: ``` https://notiflows.com/docs/cli/commands.md https://notiflows.com/docs/concepts/notiflows.md ``` This serves the page's underlying markdown as `text/markdown`, so you can fetch a single page directly into an assistant's context (the "copy for LLM" and "view markdown" actions at the top of each page use the same source). Use it when you want one page rather than the whole `llms-full.txt`. ## Related - [Notiflows for AI](/docs/ai) — the AI surfaces overview. - [Agent skills](/docs/ai/skills) — installable, grounded context packets for coding agents. - [MCP server](/docs/ai/mcp) — connect an AI editor directly to the Management API. - [Agent toolkit](/docs/ai/agent-toolkit) — turn notiflows into typed tools for your own agents. - [API Reference](/docs/api) — the interactive REST reference (fetch directly, not via `llms-full.txt`). --- # MCP Server > Connect the hosted Notiflows MCP server to Claude Code, Cursor, or Claude Desktop and manage, publish, and trigger notiflows conversationally over the Model Context Protocol. Source: https://notiflows.com/docs/ai/mcp --- title: MCP Server description: Connect the hosted Notiflows MCP server to Claude Code, Cursor, or Claude Desktop and manage, publish, and trigger notiflows conversationally over the Model Context Protocol. --- The Notiflows MCP server is a hosted [Model Context Protocol](https://modelcontextprotocol.io) server that exposes the [Management API](/docs/api/management) as MCP tools. It runs at `https://api.notiflows.com/mcp`, so any MCP-capable client — Claude Code, Cursor, Claude Desktop, and others — can list, inspect, create, validate, publish, and trigger notiflows by talking to an AI assistant. It is a thin layer over the same Management API the [CLI](/docs/cli) uses: same [account token](/docs/cli/account-tokens), same flat-steps notiflow shape, same server-side validation. Like every Notiflows AI surface, it runs inside *your* assistant on *your* model — the server only carries your API calls, never an LLM. ## Authenticate The server authenticates with a Notiflows **account token** (prefix `nf_at_`), sent as a bearer token: `Authorization: Bearer nf_at_…`. Create one in the dashboard under **Account → Account tokens**, and name it after the assistant using it (e.g. `claude-mcp`) so its actions stay auditable. The token is **account-scoped**, so a single connection can manage every project in the account. You pick the target project per call — see [Multiple projects](#multiple-projects). The account token carries full read/write access through the Management API. There are no per-project tokens or fine-grained scopes today — treat it as a privileged credential, store it in a secret manager, and revoke it if it leaks. See [Account tokens](/docs/cli/account-tokens). ## Connect Editors speak MCP over stdio, so they reach the hosted HTTP endpoint through [`mcp-remote`](https://www.npmjs.com/package/mcp-remote) — a small npx package that bridges the editor's stdio transport to a remote MCP server and forwards the `Authorization` header. **Node.js 18+** is required (for `npx`). ### Claude Code ```bash claude mcp add notiflows -- npx -y mcp-remote https://api.notiflows.com/mcp --header "Authorization: Bearer nf_at_YOUR_TOKEN" ``` ### Cursor / Claude Desktop Add the server to your client's MCP config — `.cursor/mcp.json` (Cursor) or `claude_desktop_config.json` (Claude Desktop): ```json { "mcpServers": { "notiflows": { "command": "npx", "args": ["-y", "mcp-remote", "https://api.notiflows.com/mcp", "--header", "Authorization: Bearer nf_at_YOUR_TOKEN"] } } } ``` Replace `nf_at_YOUR_TOKEN` with your account token. Restart the editor (or reload its MCP servers) and the Notiflows tools become available to the assistant. ## Multiple projects Because the account token reaches every project, one connection manages all of them. Project-scoped tools take an optional `project` argument — a project **slug**. There is **no implicit default** on the hosted server: omit `project` and the tool returns an error asking you to supply one. Call `list_projects` to discover the slugs your token can access, then pass one explicitly: ```json list_notiflows {"project": "marketing"} ``` In practice the assistant carries the project across a conversation once you've named it, so you usually set it once ("work in the `marketing` project") and let the model thread it through. ## Tools The server exposes 17 tools. Each maps to a verified Management API endpoint. **Identity** - `whoami` — verify the token and return the authenticated account. - `list_projects` — list the projects the account token can access, with their slugs. **Notiflows** - `list_notiflows` — list notiflows in the project (summaries, no steps). - `get_notiflow` — fetch a notiflow by handle, including its current and published version steps. - `upsert_notiflow` — create or update a notiflow (creates a new draft version). Send the **full** flat steps array, including the terminal `trigger` and `end` steps; handles are lowercase letters, digits, and hyphens only. - `validate_notiflow` — validate the current draft on the server. - `publish_notiflow` — publish the current draft so it becomes live for execution. - `activate_notiflow` — toggle whether the notiflow can be triggered (the active flag). - `rollback_notiflow` — discard the unpublished draft and revert to the published version. - `run_notiflow` — trigger a run. Requires either `recipients` or a `topic`. Runs the **published** version by default; pass `draft: true` to test-run the current unpublished draft. Attributed to the account token (auditable). Returns `notiflow_run_id`. **Versions** - `list_versions` — list a notiflow's version history. - `get_version` — fetch a specific version (with steps). **Templates** - `get_template` — read a channel step's template. - `upsert_template` — create or update a channel step's template (body must be non-empty). **Channels** (read-only — configured in the dashboard) - `list_channels` — list the project's delivery channels. - `get_channel` — fetch a channel by handle. **Docs** - `search_docs` — search the Notiflows documentation. **Safe by design.** There are no delete or archive tools, and channels are read-only. `rollback_notiflow` (which discards an unpublished draft) is the only semi-destructive operation, so an assistant can create and ship freely without risking data loss. ## What you can ask With the server connected, you describe the outcome and let the assistant drive the tools: - "List the projects I have access to, then list the notiflows in `marketing`." - "Create a welcome email notiflow in `acme` that sends an email, waits a day, then sends a follow-up." - "Publish the `order-shipped` notiflow." - "Run `welcome-series` for `user_123` with `first_name` set to Ada." - "Show me the version history of `order-shipped` and roll back the draft." ## Related - [CLI](/docs/cli) — the same operations at the terminal and in CI. - [Agent toolkit](/docs/ai/agent-toolkit) — give your own agents the ability to trigger notiflows. - [Agent skills](/docs/ai/skills) — teach your assistant the notiflow schemas and CLI. - [llms.txt & docs for agents](/docs/ai/llms-txt) — machine-readable docs for grounding an assistant. - [Notiflows for AI](/docs/ai) — the AI surfaces overview. --- # Agent Skills > Install the Notiflows agent skills to teach any compatible AI assistant the notiflow and template schemas, the CLI, and where authoritative docs live — grounded in the real product. Source: https://notiflows.com/docs/ai/skills --- title: Agent Skills description: Install the Notiflows agent skills to teach any compatible AI assistant the notiflow and template schemas, the CLI, and where authoritative docs live — grounded in the real product. --- Agent **skills** are installable, self-contained packets of context that teach a compatible AI coding agent (Claude Code, Cursor, and others) how to create and manage notiflows **as code** — without you pasting in docs or examples. They're grounded in the real Notiflows schemas and the [`@notiflows/cli`](/docs/cli). The skills are open source and published at [github.com/notiflows/skills](https://github.com/notiflows/skills). ## What is a skill? A skill is a `SKILL.md` file with YAML frontmatter (`name` + `description`) and a **progressive-disclosure** body: a start-here overview the agent reads first, then deeper references it pulls in only when the task needs them. The agent loads a skill when the task matches its `description`, keeping its context lean until the knowledge is actually required. Each skill follows the [agentskills.io](https://agentskills.io) spec. ## Install There are two ways to install, depending on your agent. ### Cursor and other agents Add the skills with the `skills` CLI: ```bash npx skills add notiflows/skills ``` ### Claude Code Install the Notiflows plugin from the marketplace: ```bash /plugin marketplace add notiflows/skills /plugin install notiflows@notiflows-skills ``` Or clone the repo and point Claude Code at it directly: ```bash git clone https://github.com/notiflows/skills claude --plugin-dir ./skills ``` The Claude Code plugin bundles the skills **and** the hosted [MCP server](/docs/ai/mcp), wiring it up for you. On first use it prompts once for your [account token](/docs/cli/account-tokens) and stores it in the OS keychain. ## The skills | Skill | What it covers | |---|---| | **notiflows-schema** | The `notiflow.json` flat-steps format — step types (trigger/end/channel/wait/digest/throttle/condition), positions and ordering, branching, condition steps and branches, per-step gate conditions, and per-type settings. | | **notiflows-template-schema** | Channel templates — the `{ channel_type, data }` shape, the per-channel fields, `content_type` enums, and the `@`-suffix convention that extracts a body into its own file. | | **notiflows-cli** | The [`@notiflows/cli`](/docs/cli) — install, account-token auth, the `pull` / `push` / `publish` / `validate` / `diff` commands, and `notiflow run` to trigger a notiflow. | | **notiflows-docs-support** | Where to find authoritative answers — the docs, `llms.txt`, which API and auth to reach for, and when to use the CLI versus the MCP server. | Every skill is grounded in the real Notiflows product. They describe only fields, commands, and behavior that actually exist; when a detail is uncertain at runtime, they direct the agent to validate against the Management API rather than guess. ## Related - [CLI](/docs/cli) — the commands the `notiflows-cli` skill describes. - [MCP server](/docs/ai/mcp) — connect an agent directly to the Management API. - [Agent toolkit](/docs/ai/agent-toolkit) — expose notiflows as typed tools for your own agents. - [llms.txt & docs for agents](/docs/ai/llms-txt) — the machine-readable docs the skills point to. --- # Changelog > Track updates, new features, and improvements to Notiflows Source: https://notiflows.com/docs/changelog --- title: Changelog description: Track updates, new features, and improvements to Notiflows --- # Changelog Stay up to date with the latest updates, new features, and improvements to Notiflows. --- ## August 10, 2026 A fresh coat of paint and a new SMS provider: the dashboard gets a full visual refresh with dark mode, and SMSAPI joins the SMS channel. ### New Providers - **SMSAPI** - Send SMS through SMSAPI, a leading SMS gateway in Poland and Central Europe. [Learn more](/docs/channels-providers/sms/smsapi). ### Dashboard - **Dark mode** - The dashboard now supports light, dark, and system themes — switch anytime from the user menu. The Notiflow Designer, template editors, code snippets, and the in-app feed all follow your theme. - **Refreshed design** - A modernized visual language across the app: cleaner navigation, sharper forms and tables, and semantic status colors so runs, notifications, and deliveries read at a glance. - **Command palette** - Press ⌘K (Ctrl+K on Windows/Linux) to jump to any page, switch projects, or change the theme. --- ## June 30, 2026 Notiflows is now AI-native — a full set of developer surfaces for building, managing, and triggering notiflows with AI. Your AI, your tokens: every surface runs on your own model and keys. ### AI & Developer Surfaces - **CLI** - `@notiflows/cli` — pull, edit, validate, diff, and push notiflows as code, plus `notiflow run` to trigger them. [Learn more](/docs/cli). - **MCP server** - Manage notiflows conversationally from Claude Code, Cursor, or Claude Desktop. [Learn more](/docs/ai/mcp). - **Agent toolkit** - `@notiflows/agent-toolkit` — turn published notiflows into typed tools for your own AI agents (Vercel AI SDK, OpenAI, LangChain). [Learn more](/docs/ai/agent-toolkit). - **Agent skills** - Installable skills that teach any compatible assistant the Notiflows schemas and CLI. [Learn more](/docs/ai/skills). - **llms.txt & docs for agents** - Machine-readable docs — `llms.txt`, `llms-full.txt`, and raw-markdown doc routes — for grounding an assistant. [Learn more](/docs/ai/llms-txt). --- ## April 14, 2026 Smoother sign-in, better email creation, and reliability fixes for chat steps. ### Authentication - **Passwordless login** - Replaced password-based auth with email one-time codes. Sign in and sign up now share a single flow — enter your email, get an 8-digit code, you're in. - **Bot protection** - Cloudflare Turnstile gates new account creation without adding friction for returning users. ### SDK Releases - **@notiflows/react 0.2.2** - Improved customization with optional avatar rendering and user preferences support. [View on npm](https://www.npmjs.com/package/@notiflows/react). ### Improvements - **Email template editor** - Refined editor experience with better preview rendering. - **Chat step templates** - Fixed Markdown conversion edge cases when rendering chat step content. --- ## March 19, 2026 Expanded channel support with new chat providers, email providers, and a dedicated webhook channel. ### New Providers - **WhatsApp** - Send notifications via the WhatsApp Business Cloud API - **Telegram** - Send messages to users and groups via the Telegram Bot API - **Discord** - Deliver notifications to Discord channels via bot - **Postmark** - Fast, reliable transactional email delivery - **MailerSend** - Email API with built-in templates and analytics - **SMTP** - Connect any email provider using standard SMTP credentials ### New Channel Type - **Webhook** - New dedicated channel type for sending notifications to any HTTP endpoint. URL, method, headers, and body are configured per-step with full Liquid templating support. Optional HMAC-SHA256 request signing at the channel level. ### Improvements - **Mobile Push Settings** - Added user channel settings for APNs and FCM device token management - **Slack Settings** - Fixed Slack user channel settings for workspace and user ID configuration --- ## March 15, 2026 New channels, in-app action types, better observability, and user-level notification management. ### Features #### In-App Action Types - **Action Types** - In-app notifications now support three action types: Default (clickable link), Single Action (one button), and Multi Action (primary + secondary buttons) with Liquid templating for dynamic URLs and labels #### SDK Releases - **@notiflows/client** - Updated `FeedEntryData` type with `action_type`, `primary_action`, and `secondary_action` fields - **@notiflows/react** - `Notification` component renders action buttons based on action type with new `.nf-notification-btn-primary` and `.nf-notification-btn-secondary` styles #### Web Push Channel - **Web Push Provider** - New channel type using the W3C Web Push Protocol (RFC 8030/8291/8292) with VAPID authentication - **Web Push Preview** - Live preview of web push notifications in the template editor - **Subscription Management** - Per-user, per-channel push subscriptions with automatic cleanup of expired endpoints (410 Gone) and a limit of 25 subscriptions per user per channel #### Channel Settings in User Profile - **User Channel Settings Page** - Dedicated "Channels" page under user profile showing all configured channels with provider-specific settings - **Admin & User APIs** - Full CRUD for channel settings via both the Admin API and User API #### Delivery Logs - **Delivery Log Viewer** - New "Logs" tab on the delivery detail page showing a chronological list of all send attempts - **Request/Response Inspection** - Expandable accordion view with line-numbered JSON for debugging request payloads and provider responses - **Provider-Specific Logging** - Structured logs for all providers: Web Push, APNs, FCM, SES, SendGrid, Resend, Mailgun, Slack, and webhooks ### Fixes - **APNs Error Logging** - APNs sender now captures and classifies 18 distinct error codes (BadCertificate, Unregistered, etc.) with human-readable messages and proper delivery log entries - **FCM Error Logging** - FCM sender now logs 7 error types (INVALID_ARGUMENT, UNREGISTERED, QUOTA_EXCEEDED, etc.) with structured delivery log entries instead of silent failures --- ## Public Launch - February 26, 2026 Notiflows is now open to the public. After months of private beta testing and invaluable feedback from early adopters, we're opening the doors to everyone. This release brings major new capabilities across the platform. ### Features #### Journey Builder - **Visual Journey Builder** - Design multi-step notification campaigns with conditional branching, delays, digests, and throttling - **Condition Steps** - Route users through different notification paths based on event data, user attributes, or custom logic - **Wait Steps** - Add timed delays between notification steps - **Digest Steps** - Group multiple events into a single notification over a configurable time window - **Throttle Steps** - Rate-limit notifications per recipient to prevent fatigue - **Copy-on-Write Versioning** - Draft, publish, and archive notiflow versions with full history #### New Server-side SDKs - **Python SDK** - Production-ready SDK available via `pip install notiflows` - **Ruby SDK** - Production-ready SDK available via `gem install notiflows` - **Node.js SDK** - Updated with full API coverage and improved error handling --- ## Private Beta - November 2025 After months of development and internal testing, we're excited to open Notiflows to a select group of early adopters. This private beta marks the beginning of our journey to provide the most comprehensive notification infrastructure for modern applications. ### Features #### Core Platform - **Multi-channel Notification Orchestration** - Build sophisticated notiflows that span email, SMS, mobile push, in-app, and chat channels - **Visual Notiflow Builder** - Design complex notiflows with an intuitive drag-and-drop interface - **Users & Recipients** - Manage your users and recipients with flexible data models and attributes - **Template Management** - Create, manage, and version notification templates with support for dynamic content and localization - **User Preferences** - Give your users granular control over their notification preferences across all channels - **In-app Inbox** - Real-time in-app notification inbox with read/unread states, archiving, and filtering - **Broadcast System** - Send topic-based broadcasts with subscription management and targeting capabilities #### Channels & Integrations - **Email Providers** - Support for SendGrid, AWS SES, Mailgun, and Resend - **SMS Providers** - Integration with Twilio - **Mobile Push** - Firebase Cloud Messaging (FCM) and Apple Push Notification Service (APNs) - **Chat Platforms** - Slack integration for team notifications #### Developer Experience - **Server-side SDKs** - Production-ready SDK for Node.js - **Client-side SDKs** - React and vanilla JavaScript SDKs for in-app experiences - **REST API** - Comprehensive RESTful API with OpenAPI specification - **API Documentation** - Interactive API reference with code examples and playground #### Enterprise Features - **Multi-tenancy** - Full workspace isolation with team management and role-based access control - **Analytics & Reporting** - Detailed delivery metrics, engagement tracking, and custom reports - **Rate Limiting** - Intelligent rate limiting to prevent notification fatigue - **Failover & Retry** - Automatic failover between providers with configurable retry policies ### Infrastructure - **High Availability** - 99.9% uptime SLA with multi-region deployment - **Scalability** - Built to handle millions of notifications per day - **Security** - Encryption at rest and in transit ### Documentation - **Getting Started Guide** - Comprehensive onboarding documentation - **Concept Guides** - In-depth explanations of core concepts and best practices - **Channel Configuration** - Step-by-step setup guides for all supported channels - **SDK Documentation** - Complete reference for all server and client SDKs - **API Reference** - Auto-generated API documentation from OpenAPI specs ### Developer Tools - **Monitoring Dashboard** - Real-time visibility into notification delivery and performance --- ## Coming Soon We're constantly working to improve Notiflows. Here's what's on our roadmap: - **Testing Tools** - Sandbox environment and testing utilities - **Debug Mode** - Detailed logging and troubleshooting capabilities - **Performance Metrics** - Advanced analytics for API response times and delivery tracking - **Webhooks** - Real-time event notifications for delivery status and user actions - **Additional Email Providers** - Postmark, SMTP, and more - **Additional SMS Providers** - MessageBird, Vonage, and more - **More SDKs** - PHP, Go, Java, Vue, Angular, and native mobile SDKs - **A/B Testing** - Test different notification strategies and templates - **Advanced Segmentation** - More powerful user targeting and segmentation - **Compliance Tools** - GDPR compliance features, data retention policies, and audit logs - **SOC 2 Type II Certification** - Enterprise-grade security compliance --- Have feedback or feature requests? We'd love to hear from you! Reach out to us at [contact@notiflows.com](mailto:contact@notiflows.com). --- # Overview > Channels and providers in Notiflows Source: https://notiflows.com/docs/channels-providers --- title: Overview description: Channels and providers in Notiflows --- ## Channels & Providers Overview Channels and providers form the delivery infrastructure of Notiflows, enabling you to reach users across multiple communication methods. Understanding how they work together is essential for building a reliable notification system. ### What are Channels? Channels represent the different communication methods available for delivering notifications. Each channel type serves a specific purpose and reaches users in different contexts: - **In-App**: Notifications displayed directly within your application - **Email**: Messages delivered to users' email inboxes - **SMS**: Text messages sent to mobile phones - **Mobile Push**: Notifications pushed to mobile devices - **Chat**: Messages sent to team collaboration platforms - **Web Push**: Notifications delivered through web browsers ### What are Providers? Providers are the external services that actually deliver your notifications. Each channel type can have one or more providers configured. For example: - Email channels might use SendGrid, Amazon SES, or Mailgun - SMS channels might use Twilio or SMSAPI - Push channels might use Firebase Cloud Messaging or Apple Push Notification Service The In-App channel is unique—it's handled internally by Notiflows and doesn't require external providers. ### How Channels and Providers Work Together When a notiflow triggers a notification: 1. **Channel Selection**: The notiflow determines which channels to use based on the configured steps and user preferences 2. **Provider Routing**: For each channel, Notiflows routes the notification to the configured provider 3. **Delivery**: The provider handles the actual delivery to the user 4. **Tracking**: Notiflows monitors delivery status and updates your notification records ### Environment Configuration Providers are configured per environment (development, staging, production). This separation ensures: - Test notifications don't accidentally reach production users - Different credentials can be used for each environment - Provider settings can be optimized for each environment's needs ### Available Integrations Notiflows supports a comprehensive range of providers across all channel types: | Channel | Supported Providers | |---------|---------------------| | In-App | Notiflows In-App (built-in) | | Email | Amazon SES, SendGrid, Resend, Mailgun | | SMS | Twilio, SMSAPI | | Mobile Push | Firebase Cloud Messaging (FCM), Apple Push Notification Service (APNs) | | Chat | Slack, WhatsApp, Telegram, Discord | | Web Push | Web Push (VAPID) | | Webhook | HTTP endpoint (any URL) | ### Setting Up Providers To configure a provider: 1. Navigate to the Notiflows Dashboard 2. Go to **Integrations** in the sidebar 3. Click **Add Provider** 4. Select the channel type and provider 5. Enter the required credentials 6. Save and activate the integration Each provider has specific credential requirements, which are detailed in the individual provider documentation pages. ### Best Practices **Start with Essential Channels** Begin with the channels most important to your users. Email and in-app notifications are often good starting points before expanding to push and SMS. **Test Thoroughly** Always test provider configurations in development before deploying to production. Verify credentials, sender information, and delivery. **Monitor Delivery** Keep track of delivery rates and failures across providers. This helps identify issues early and optimize your notification strategy. **Plan for Redundancy** Consider configuring backup providers for critical channels to ensure delivery even if a primary provider experiences issues. ### Next Steps Explore the documentation for each channel type to learn about specific configuration requirements, best practices, and provider-specific features. --- # Overview > Web push notifications Source: https://notiflows.com/docs/channels-providers/browser-push --- title: Overview description: Web push notifications --- Web Push enables you to send push notifications directly to web browsers, even when users aren't actively on your site. This is perfect for: - Re-engagement notifications - Breaking news and updates - Time-sensitive alerts - Abandoned cart reminders - New content notifications For setup instructions, see the [Web Push channel documentation](/docs/channels-providers/web-push). ### How Web Push Works 1. **Permission Request**: User grants permission in their browser 2. **Subscription**: Browser generates a unique push subscription 3. **Storage**: Subscription is stored with the user's profile via the User API 4. **Delivery**: Notifications are sent via the browser's push service using VAPID 5. **Display**: Browser shows the notification even if the site is closed ### Browser Support | Browser | Status | |---------|--------| | Chrome | Supported | | Firefox | Supported | | Safari | Supported | | Edge | Supported | --- # Overview > Send notifications to chat platforms Source: https://notiflows.com/docs/channels-providers/chat --- title: Overview description: Send notifications to chat platforms --- Chat is a channel type for sending notifications to team communication platforms. ## Supported Providers Notifications to Slack workspaces Messages via WhatsApp Business API Messages via Telegram Bot API Messages to Discord channels via bot ## Templates Chat templates support two content types: - **Markdown** - Simple markdown formatting - **JSON** - Rich message format (e.g., Slack Block Kit) Use Liquid templating for dynamic content: ```liquid New order from {{ actor.first_name }}: #{{ data.order_id }} ``` Or use JSON for rich formatting: ```json { "blocks": [ { "type": "section", "text": { "type": "mrkdwn", "text": "*Order #{{ data.order_id }}*\nCustomer: {{ actor.first_name }}" } } ] } ``` Available variable contexts: - `recipient.*` - Recipient user data - `actor.*` - User who triggered the notification - `data.*` - Custom payload passed when triggering the notiflow --- # Discord > Configure Discord as a chat provider in Notiflows Source: https://notiflows.com/docs/channels-providers/chat/discord --- title: Discord description: Configure Discord as a chat provider in Notiflows --- Discord integration enables notifications to be sent to Discord channels and servers via the Discord Bot API. ## Configuration To create a chat channel with Discord, configure the following: | Field | Required | Description | |-------|----------|-------------| | Bot Token | Yes | Discord bot token from the Developer Portal | ## Prerequisites Before configuring Discord in Notiflows: 1. A Discord server where you have admin permissions 2. A Discord application created at the [Developer Portal](https://discord.com/developers/applications) ## Creating a Discord Bot 1. Go to [discord.com/developers/applications](https://discord.com/developers/applications) 2. Click **New Application** and give it a name 3. Navigate to **Bot** in the left sidebar 4. Click **Reset Token** to generate a new bot token — copy it immediately 5. Under **Privileged Gateway Intents**, enable **Message Content Intent** if you need to read messages 6. Navigate to **OAuth2** > **URL Generator** 7. Select the `bot` scope and the following permissions: - `Send Messages` - `Embed Links` - `Attach Files` (optional, for rich media) 8. Copy the generated URL and open it to invite the bot to your server ## Setup in Notiflows 1. Navigate to **Channels** in your project 2. Click **Create Channel** 3. Select **Chat** as the channel type 4. Select **Discord** as the provider 5. Paste your Bot Token 6. Save the channel ## User Channel Settings To send Discord notifications to a user, set their target channel via the Admin API: ```bash curl -X PUT /api/admin/v1/users/:user_external_id/channel-settings/:channel_id \ -d '{ "settings": { "discord_channel_id": "1234567890123456789" } }' ``` | Field | Description | |-------|-------------| | `discord_channel_id` | Discord channel ID where messages will be sent | To find a channel ID, enable **Developer Mode** in Discord settings, then right-click a channel and select **Copy Channel ID**. ## Templates Chat templates support two content types: - **Markdown** — Discord-flavored markdown formatting - **JSON** — Discord embed format for rich messages Use Liquid templating for dynamic content: ```liquid New deployment by {{ actor.first_name }}: **{{ data.service }}** → `{{ data.version }}` ``` For rich embeds, use JSON content type: ```json { "title": "Deployment Complete", "description": "{{ data.service }} deployed to {{ data.environment }}", "color": 3066993 } ``` Available variable contexts: - `recipient.*` — Recipient user data - `actor.*` — User who triggered the notification - `data.*` — Custom payload passed when triggering the notiflow --- # Slack > Configure Slack as a chat provider in Notiflows Source: https://notiflows.com/docs/channels-providers/chat/slack --- title: Slack description: Configure Slack as a chat provider in Notiflows --- Slack integration enables notifications to be sent as direct messages or to channels in your Slack workspace. ## Configuration To create a chat channel with Slack, configure the following: | Field | Required | Description | |-------|----------|-------------| | Bot Token | Yes | Bot User OAuth Token (starts with `xoxb-`) | ## Prerequisites Before configuring Slack in Notiflows: 1. A Slack workspace 2. A Slack app created at [api.slack.com/apps](https://api.slack.com/apps) ## Creating a Slack App 1. Go to [api.slack.com/apps](https://api.slack.com/apps) 2. Click **Create New App** > **From scratch** 3. Enter an app name and select your workspace 4. Go to **OAuth & Permissions** and add the following bot token scopes: - `chat:write` — Send messages - `chat:write.public` — Send messages to channels without joining - `channels:read` — List public channels - `groups:read` — List private channels - `users:read` — Look up users (optional, for user ID resolution) - `users:read.email` — Look up users by email (optional) 5. Click **Install to Workspace** and authorize the app 6. Copy the **Bot User OAuth Token** (`xoxb-...`) ## Setup in Notiflows 1. Navigate to **Channels** in your project 2. Click **Create Channel** 3. Select **Chat** as the channel type 4. Select **Slack** as the provider 5. Paste your Bot Token 6. Save the channel ## User Channel Settings To send Slack notifications to a user, set their Slack channel settings via the Admin API: ```bash curl -X PUT /api/admin/v1/users/:user_external_id/channel-settings/:channel_id \ -d '{ "settings": { "slack_user_id": "U0123456789" } }' ``` | Field | Description | |-------|-------------| | `slack_user_id` | Slack member ID for direct messages (starts with `U`) | | `slack_channel_id` | Slack channel ID for channel messages (starts with `C`) | At least one must be provided. To find a user's Slack member ID, open their profile in Slack and click **Copy member ID**. ## Templates Chat templates support two content types: - **Markdown** — Simple markdown formatting, converted to Slack's `mrkdwn` format - **JSON** — Slack Block Kit format for rich messages Use Liquid templating for dynamic content: ```liquid New order from {{ actor.first_name }}: #{{ data.order_id }} ``` Available variable contexts: - `recipient.*` — Recipient user data - `actor.*` — User who triggered the notification - `data.*` — Custom payload passed when triggering the notiflow --- # Telegram > Configure Telegram as a chat provider in Notiflows Source: https://notiflows.com/docs/channels-providers/chat/telegram --- title: Telegram description: Configure Telegram as a chat provider in Notiflows --- Telegram integration enables notifications via the Telegram Bot API, delivering messages to users and groups on Telegram. ## Configuration To create a chat channel with Telegram, configure the following: | Field | Required | Description | |-------|----------|-------------| | Bot Token | Yes | Bot token from BotFather | ## Prerequisites Before configuring Telegram in Notiflows: 1. A Telegram account 2. A Telegram bot created via [@BotFather](https://t.me/botfather) ## Creating a Telegram Bot 1. Open Telegram and search for **@BotFather** 2. Send `/newbot` and follow the prompts to name your bot 3. BotFather will reply with your **bot token** (e.g., `123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11`) 4. Copy the token — you'll need it for the Notiflows configuration 5. Optionally, send `/setdescription` to set a description for your bot 6. Add the bot to any group or channel where it should send messages ## Setup in Notiflows 1. Navigate to **Channels** in your project 2. Click **Create Channel** 3. Select **Chat** as the channel type 4. Select **Telegram** as the provider 5. Paste your Bot Token 6. Save the channel ## User Channel Settings To send Telegram notifications to a user, set their chat ID via the Admin API: ```bash curl -X PUT /api/admin/v1/users/:user_external_id/channel-settings/:channel_id \ -d '{ "settings": { "chat_id": "123456789" } }' ``` | Field | Description | |-------|-------------| | `chat_id` | Telegram chat ID for the user, group, or channel | To find a chat ID: - For **users**: the user can message your bot, then check `getUpdates` API - For **groups**: add the bot to the group, send a message, and check `getUpdates` - For **channels**: use the channel's `@username` prefixed with `@`, or the numeric ID ## Templates Chat templates support two content types: - **Markdown** — Telegram MarkdownV2 formatting - **Plain text** — Simple text messages Use Liquid templating for dynamic content: ```liquid New order from {{ actor.first_name }}: #{{ data.order_id }} Total: {{ data.total }} ``` Available variable contexts: - `recipient.*` — Recipient user data - `actor.*` — User who triggered the notification - `data.*` — Custom payload passed when triggering the notiflow --- # WhatsApp > Configure WhatsApp as a chat provider in Notiflows Source: https://notiflows.com/docs/channels-providers/chat/whatsapp --- title: WhatsApp description: Configure WhatsApp as a chat provider in Notiflows --- WhatsApp integration enables notifications via the WhatsApp Business Cloud API, reaching users on the world's most popular messaging platform. ## Configuration To create a chat channel with WhatsApp, configure the following: | Field | Required | Description | |-------|----------|-------------| | Access Token | Yes | Permanent access token from Meta Business | | Phone Number ID | Yes | WhatsApp Business phone number ID | | Business Account ID | Yes | WhatsApp Business Account ID | ## Prerequisites Before configuring WhatsApp in Notiflows: 1. A [Meta Business account](https://business.facebook.com/) 2. A WhatsApp Business account linked to your Meta Business 3. A registered phone number in the WhatsApp Business Platform ## Getting Your Credentials 1. Go to [Meta for Developers](https://developers.facebook.com/) 2. Create or select an app with **WhatsApp** product enabled 3. Navigate to **WhatsApp** > **API Setup** 4. Note your **Phone Number ID** and **WhatsApp Business Account ID** 5. Generate a **permanent access token** under **System Users** in Business Settings: - Go to **Business Settings** > **System Users** - Create a system user with **Admin** role - Generate a token with the `whatsapp_business_messaging` permission ## Setup in Notiflows 1. Navigate to **Channels** in your project 2. Click **Create Channel** 3. Select **Chat** as the channel type 4. Select **WhatsApp** as the provider 5. Enter your Access Token, Phone Number ID, and Business Account ID 6. Save the channel ## User Channel Settings To send WhatsApp notifications to a user, set their phone number via the Admin API: ```bash curl -X PUT /api/admin/v1/users/:user_external_id/channel-settings/:channel_id \ -d '{ "settings": { "phone": "+1234567890" } }' ``` | Field | Description | |-------|-------------| | `phone` | Recipient's phone number in E.164 format (e.g., `+1234567890`) | ## Templates WhatsApp messages are sent as text messages. Use Liquid templating for dynamic content: ```liquid Order #{{ data.order_id }} has been shipped! Track at {{ data.tracking_url }} ``` Available variable contexts: - `recipient.*` — Recipient user data - `actor.*` — User who triggered the notification - `data.*` — Custom payload passed when triggering the notiflow ## Important Notes - WhatsApp enforces a **24-hour messaging window**. You can only send free-form messages to users who have messaged you in the last 24 hours. Outside this window, you must use approved message templates via the Meta Business Manager. - Messages are sent as plain text via the WhatsApp Cloud API `messages` endpoint. --- # Overview > Send email notifications through various providers Source: https://notiflows.com/docs/channels-providers/email --- title: Overview description: Send email notifications through various providers --- Email is a channel type for sending transactional and marketing emails. ## Supported Providers Cost-effective email built on AWS infrastructure Email delivery with analytics and tracking Modern email API for developers Email API with US and EU regions ## Templates Email templates support three content types: - **Visual** - Drag-and-drop editor for rich HTML emails - **HTML** - Raw HTML with full control - **Plaintext** - Simple text emails Use Liquid templating for dynamic content: ```liquid Hi {{ recipient.first_name }}, {{ data.message }} Best, {{ actor.first_name }} ``` Available variable contexts: - `recipient.*` - Recipient user data (first_name, last_name, email, etc.) - `actor.*` - User who triggered the notification - `data.*` - Custom payload passed when triggering the notiflow --- # MailerSend > Configure MailerSend as an email provider in Notiflows Source: https://notiflows.com/docs/channels-providers/email/mailersend --- title: MailerSend description: Configure MailerSend as an email provider in Notiflows --- MailerSend is an intuitive email API with built-in templates and analytics. ## Prerequisites Before configuring MailerSend in Notiflows, you need: 1. A [MailerSend account](https://www.mailersend.com) 2. A verified domain 3. An API token ## Configuration | Field | Required | Description | |-------|----------|-------------| | API Key | Yes | MailerSend API token | | From Email | Yes | An email address on your verified domain | | From Name | No | Display name shown to recipients | ## Step 1: Add and verify a domain 1. Log in to the [MailerSend dashboard](https://app.mailersend.com) 2. Go to **Domains** 3. Click **Add domain** 4. Enter your domain name 5. Add the provided DNS records (SPF, DKIM, CNAME) 6. Click **Verify** once records are added ## Step 2: Create an API token 1. In the MailerSend dashboard, go to **API Tokens** 2. Click **Generate new token** 3. Enter a name (e.g., `Notiflows`) 4. Select **Full access** or **Custom** with email sending permissions 5. Click **Create** 6. Copy the token immediately MailerSend only displays the API token once. Copy it and store it securely. ## Step 3: Set up in Notiflows 1. Navigate to **Channels** in your project 2. Click **Create Channel** 3. Select **Email** as the channel type 4. Select **MailerSend** as the provider 5. Paste your **API Key** 6. Enter your verified **From Email** and optionally a **From Name** 7. Save the channel ## Templates Email templates support three content types: - **Visual** - Drag-and-drop editor for rich HTML emails - **HTML** - Raw HTML with full control - **Plaintext** - Simple text emails Use Liquid templating for dynamic content: ```liquid Hi {{ recipient.first_name }}, {{ data.message }} ``` Available variable contexts: - `recipient.*` - Recipient user data (first_name, last_name, email, etc.) - `actor.*` - User who triggered the notification - `data.*` - Custom payload passed when triggering the notiflow --- # Mailgun > Configure Mailgun as an email provider in Notiflows Source: https://notiflows.com/docs/channels-providers/email/mailgun --- title: Mailgun description: Configure Mailgun as an email provider in Notiflows --- Mailgun is an email API for sending, receiving, and tracking emails. ## Prerequisites Before configuring Mailgun in Notiflows, you need: 1. A [Mailgun account](https://signup.mailgun.com/new/signup) 2. A verified sending domain 3. Your private API key ## Configuration | Field | Required | Description | |-------|----------|-------------| | API Key | Yes | Mailgun private API key | | Domain | Yes | Your verified sending domain (e.g., `mg.example.com`) | | Region | Yes | **US** or **EU** — must match the region you selected when adding the domain | | From Email | Yes | Sender email address on your verified domain | | From Name | No | Display name shown to recipients (e.g., `My App`) | ## Step 1: Add and verify a domain Mailgun requires you to verify a sending domain by adding DNS records. 1. Log in to the [Mailgun dashboard](https://app.mailgun.com) 2. In the left navigation, click **Sending** > **Domains** 3. Click **Add New Domain** 4. Enter your domain name — Mailgun recommends using a subdomain like `mg.example.com` to separate transactional email from your root domain's reputation 5. Select your region: - **US** — data stored in US data centers (`api.mailgun.net`) - **EU** — data stored in EU data centers (`api.eu.mailgun.net`), recommended for GDPR compliance 6. Select a DKIM key length — **2048-bit** is recommended 7. Click **Add Domain** The region you choose is permanent for that domain. If you need to switch regions later, you'll have to add the domain again in the other region. Choose carefully based on where your recipients are located and your compliance requirements. Mailgun provides DNS records to add: | Record | Type | Purpose | |--------|------|---------| | SPF | TXT | Authorizes Mailgun to send email from your domain | | DKIM | TXT | Two records that provide public keys for email authentication | | MX | MX | Optional — enables Mailgun to receive bounces and complaints | | Tracking CNAME | CNAME | Optional — enables click and open tracking with your domain | Add the required records (SPF and DKIM) to your DNS provider: 1. Copy the records from the Mailgun domain settings page 2. Add them to your DNS provider 3. Return to Mailgun and click **Verify DNS settings** If your domain already has an SPF record, do not add a second one — DNS only allows one SPF record per domain. Instead, edit the existing record and add `include:mailgun.org` to it. For example: `v=spf1 include:_spf.google.com include:mailgun.org ~all` DNS changes typically propagate within a few hours but can take up to 48 hours. Verified domains show a green **Verified** badge in the dashboard. ## Step 2: Get your API key 1. In the Mailgun dashboard, click your account name in the top right corner 2. Select **API Security** 3. Under **Mailgun API keys**, your private API key is listed 4. Click the eye icon to reveal and copy it You can also create additional API keys scoped to specific domains from this page by clicking **Add new key**. ## Step 3: Set up in Notiflows 1. Navigate to **Channels** in your project 2. Click **Create Channel** 3. Select **Email** as the channel type 4. Select **Mailgun** as the provider 5. Paste your **API Key** 6. Enter your verified **Domain** (e.g., `mg.example.com`) 7. Select the **Region** that matches the region you chose when adding the domain in Mailgun 8. Enter your **From Email** (must be on your verified domain) and optionally a **From Name** 9. Save the channel ## Templates Email templates support three content types: - **Visual** - Drag-and-drop editor for rich HTML emails - **HTML** - Raw HTML with full control - **Plaintext** - Simple text emails Use Liquid templating for dynamic content: ```liquid Hi {{ recipient.first_name }}, {{ data.message }} ``` Available variable contexts: - `recipient.*` - Recipient user data (first_name, last_name, email, etc.) - `actor.*` - User who triggered the notification - `data.*` - Custom payload passed when triggering the notiflow --- # Postmark > Configure Postmark as an email provider in Notiflows Source: https://notiflows.com/docs/channels-providers/email/postmark --- title: Postmark description: Configure Postmark as an email provider in Notiflows --- Postmark is a fast, reliable transactional email service with detailed delivery analytics. ## Prerequisites Before configuring Postmark in Notiflows, you need: 1. A [Postmark account](https://postmarkapp.com) 2. A verified sender signature (domain or email address) 3. A Server API Token ## Configuration | Field | Required | Description | |-------|----------|-------------| | Server Token | Yes | Postmark Server API Token | | From Email | Yes | A verified sender address | | From Name | No | Display name shown to recipients | | Message Stream | No | Postmark message stream (defaults to transactional) | ## Step 1: Verify a sender signature Postmark requires a verified sender signature before you can send email. 1. Log in to the [Postmark dashboard](https://account.postmarkapp.com) 2. Go to **Sender Signatures** 3. Click **Add Domain** or **Add Sender Signature** 4. For domain verification, add the provided DNS records (DKIM and Return-Path) 5. For single email verification, confirm via the verification email ## Step 2: Get your Server API Token 1. In the Postmark dashboard, select your **Server** 2. Go to **API Tokens** 3. Copy the **Server API Token** Each Postmark server has its own API token. Make sure you're copying the token from the correct server. ## Step 3: Set up in Notiflows 1. Navigate to **Channels** in your project 2. Click **Create Channel** 3. Select **Email** as the channel type 4. Select **Postmark** as the provider 5. Paste your **Server Token** 6. Enter your verified **From Email** and optionally a **From Name** 7. Optionally set a **Message Stream** (e.g., `outbound` for transactional, or a custom stream name) 8. Save the channel ## Templates Email templates support three content types: - **Visual** - Drag-and-drop editor for rich HTML emails - **HTML** - Raw HTML with full control - **Plaintext** - Simple text emails Use Liquid templating for dynamic content: ```liquid Hi {{ recipient.first_name }}, {{ data.message }} ``` Available variable contexts: - `recipient.*` - Recipient user data (first_name, last_name, email, etc.) - `actor.*` - User who triggered the notification - `data.*` - Custom payload passed when triggering the notiflow --- # Resend > Configure Resend as an email provider in Notiflows Source: https://notiflows.com/docs/channels-providers/email/resend --- title: Resend description: Configure Resend as an email provider in Notiflows --- Resend is a modern email API designed for developers with a clean, intuitive interface. ## Prerequisites Before configuring Resend in Notiflows, you need: 1. A [Resend account](https://resend.com/signup) 2. A verified domain 3. An API key with sending access ## Configuration | Field | Required | Description | |-------|----------|-------------| | API Key | Yes | Resend API key with sending access | | From Email | Yes | An email address on your verified domain | | From Name | No | Display name shown to recipients (e.g., `My App`) | ## Step 1: Add and verify a domain Resend requires you to verify the domain you send from by adding DNS records. 1. Log in to the [Resend dashboard](https://resend.com) 2. In the left navigation, click **Domains** 3. Click **Add Domain** 4. Enter your domain name — Resend recommends using a subdomain like `updates.yourdomain.com` to separate transactional email from your root domain's reputation 5. Resend provides DNS records to add: - **SPF** (TXT record) — authorizes Resend to send from your domain - **DKIM** (TXT record) — provides a public key to verify email authenticity 6. Add both records to your DNS provider 7. Return to the Resend dashboard and click **Verify** DNS changes typically propagate within a few hours but can take up to 72 hours. If the domain isn't verified after 72 hours, the status changes to "failed" and you'll need to re-add it. ## Step 2: Create an API key 1. In the Resend dashboard, click **API Keys** in the left navigation 2. Click **Create API Key** 3. Enter a name (e.g., `Notiflows`) — maximum 50 characters 4. Select **Sending access** — this is the minimum permission needed and is recommended over full access 5. Optionally restrict the key to a specific verified domain 6. Click **Create** 7. Copy the API key immediately Resend only shows the API key once. Copy it now and store it securely. If you lose it, you'll need to create a new key. ## Step 3: Set up in Notiflows 1. Navigate to **Channels** in your project 2. Click **Create Channel** 3. Select **Email** as the channel type 4. Select **Resend** as the provider 5. Paste your **API Key** 6. Enter your verified **From Email** (must be on your verified domain) and optionally a **From Name** 7. Save the channel ## Templates Email templates support three content types: - **Visual** - Drag-and-drop editor for rich HTML emails - **HTML** - Raw HTML with full control - **Plaintext** - Simple text emails Use Liquid templating for dynamic content: ```liquid Hi {{ recipient.first_name }}, {{ data.message }} ``` Available variable contexts: - `recipient.*` - Recipient user data (first_name, last_name, email, etc.) - `actor.*` - User who triggered the notification - `data.*` - Custom payload passed when triggering the notiflow --- # SendGrid > Configure SendGrid as an email provider in Notiflows Source: https://notiflows.com/docs/channels-providers/email/sendgrid --- title: SendGrid description: Configure SendGrid as an email provider in Notiflows --- SendGrid is a cloud-based email delivery platform with analytics and deliverability tools. ## Prerequisites Before configuring SendGrid in Notiflows, you need: 1. A [SendGrid account](https://signup.sendgrid.com/) 2. A verified sender identity (domain or single sender) 3. An API key with Mail Send permissions ## Configuration | Field | Required | Description | |-------|----------|-------------| | API Key | Yes | SendGrid API key with Mail Send permissions | | From Email | Yes | A verified sender email address | | From Name | No | Display name shown to recipients (e.g., `My App`) | | Click Tracking | No | Track when recipients click links in your emails | | Open Tracking | No | Track when recipients open your emails | ## Step 1: Verify a sender identity SendGrid requires you to verify the domain or email address you send from. Domain authentication is recommended for production because it improves deliverability and covers all addresses on that domain. ### Option A: Authenticate a domain (recommended) 1. Log in to the [SendGrid dashboard](https://app.sendgrid.com) 2. In the left navigation, click **Settings** > **Sender Authentication** 3. In the **Domain Authentication** section, click **Get Started** 4. Select your DNS provider from the dropdown (or choose **Other Host** if not listed) 5. Enter your root domain (e.g., `example.com` — do not include `www` or a subdomain) 6. Ensure **Use automated security** is checked — this generates CNAME records that SendGrid manages for you 7. Click **Next** 8. SendGrid provides three CNAME records — add these to your DNS provider 9. Return to SendGrid and click **Verify** DNS changes can take up to 48 hours to propagate. If verification fails, wait and try again. Contact SendGrid support if it's still unverified after 48 hours. ### Option B: Verify a single sender Use this for quick testing or if you don't control the sending domain. 1. In the SendGrid dashboard, go to **Settings** > **Sender Authentication** 2. Click **Verify a Single Sender** 3. Click **Create New Sender** 4. Fill in the required fields: **From Name**, **From Email Address**, **Reply To**, **Business Address**, and **Nickname** 5. Click **Create** 6. Check your inbox for a confirmation email and click the verification link Single sender verification is not recommended for production. Free email addresses (gmail.com, yahoo.com, etc.) may fail DMARC checks and get rejected by recipient mail servers. ## Step 2: Create an API key 1. In the SendGrid dashboard, click **Settings** > **API Keys** 2. Click **Create API Key** 3. Enter a name (e.g., `Notiflows`) 4. Select **Restricted Access** 5. Under **Mail Send**, toggle **Mail Send** to full access 6. Click **Create & View** 7. Copy the API key immediately SendGrid only shows the API key once. Copy it now and store it securely. If you lose it, you'll need to create a new key. ## Step 3: Set up in Notiflows 1. Navigate to **Channels** in your project 2. Click **Create Channel** 3. Select **Email** as the channel type 4. Select **SendGrid** as the provider 5. Paste your **API Key** 6. Enter your verified **From Email** and optionally a **From Name** 7. Optionally enable **Click Tracking** and **Open Tracking** 8. Save the channel ## Templates Email templates support three content types: - **Visual** - Drag-and-drop editor for rich HTML emails - **HTML** - Raw HTML with full control - **Plaintext** - Simple text emails Use Liquid templating for dynamic content: ```liquid Hi {{ recipient.first_name }}, {{ data.message }} ``` Available variable contexts: - `recipient.*` - Recipient user data (first_name, last_name, email, etc.) - `actor.*` - User who triggered the notification - `data.*` - Custom payload passed when triggering the notiflow --- # Amazon SES > Configure Amazon SES as an email provider in Notiflows Source: https://notiflows.com/docs/channels-providers/email/ses --- title: Amazon SES description: Configure Amazon SES as an email provider in Notiflows --- Amazon Simple Email Service (SES) is a cost-effective email service built on AWS infrastructure. ## Prerequisites Before configuring SES in Notiflows, you need: 1. An [AWS account](https://aws.amazon.com) 2. A verified sending identity (domain or email address) 3. An IAM user with SES send permissions 4. SES moved out of sandbox mode if you need to send to unverified recipients ## Configuration | Field | Required | Description | |-------|----------|-------------| | Region | Yes | The AWS region where SES is configured (e.g., `us-east-1`) | | Access Key ID | Yes | IAM user access key | | Secret Access Key | Yes | IAM user secret key | | From Email | Yes | A verified sender email address or an address on a verified domain | | From Name | No | Display name shown to recipients (e.g., `My App`) | ## Step 1: Verify a sender identity SES requires you to verify the domain or email address you send from. Domain verification is recommended for production because it covers all addresses on that domain. ### Option A: Verify a domain (recommended) 1. Open the [Amazon SES console](https://console.aws.amazon.com/ses/) 2. In the left navigation, under **Configuration**, click **Identities** 3. Click **Create identity** 4. Select **Domain** as the identity type 5. Enter your domain (e.g., `example.com`) 6. Under **Verifying your domain**, leave **Easy DKIM** selected and choose **RSA_2048_BIT** as the signing key length 7. Click **Create identity** SES generates three CNAME records for DKIM authentication. You need to add these to your domain's DNS: 1. On the identity details page, click the **Authentication** tab 2. Expand **Publish DNS records** — you'll see three CNAME records 3. Add each record to your DNS provider (or click **Download .csv record set** to export them) 4. Wait for DNS propagation — the **Identity status** changes to **Verified** once SES detects the records (usually a few minutes, can take up to 72 hours) If you use Amazon Route 53 for DNS, SES can publish the records automatically. Check the **Publish DNS records to Route53** option during identity creation. ### Option B: Verify an email address Use this for quick testing or if you don't own the sending domain. 1. Open the [Amazon SES console](https://console.aws.amazon.com/ses/) 2. In the left navigation, under **Configuration**, click **Identities** 3. Click **Create identity** 4. Select **Email address** as the identity type 5. Enter the email address you want to send from 6. Click **Create identity** 7. Check your inbox for an email from `no-reply-aws@amazon.com` and click the verification link The identity status updates to **Verified** within a few minutes. ## Step 2: Move out of sandbox mode New SES accounts start in sandbox mode, which limits sending to verified email addresses only. For production use, you need to request production access. 1. Open the [Amazon SES console](https://console.aws.amazon.com/ses/) 2. In the left navigation, click **Account dashboard** 3. At the top you'll see a banner: "Your Amazon SES account is in the sandbox" 4. Click **View Get set up page**, then **Request production access** 5. Fill in the form: - **Mail Type** — select **Transactional** - **Website URL** — your application's URL - **Additional Contacts** — email addresses for account communications - Acknowledge that you'll only send to recipients who have opted in and that you handle bounces and complaints 6. Click **Submit request** AWS typically responds within 24 hours. ## Step 3: Create an IAM user Notiflows needs an IAM access key with permission to send email through SES. Create a dedicated IAM user rather than using root account credentials. ### Create the IAM policy 1. Open the [IAM console](https://console.aws.amazon.com/iam) 2. In the left navigation, click **Policies** 3. Click **Create policy** 4. Click the **JSON** tab and paste the following: ```json { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "ses:SendEmail", "ses:SendRawEmail" ], "Resource": "*" } ] } ``` 5. Click **Next** 6. Name the policy (e.g., `NotiflowsSESSendEmail`) 7. Click **Create policy** To restrict sending to a specific verified identity, replace `"Resource": "*"` with your identity ARN: ```json "Resource": "arn:aws:ses:us-east-1:123456789012:identity/example.com" ``` You can find your identity ARN on the identity details page in the SES console. ### Create the user and attach the policy 1. In the IAM console, click **Users** in the left navigation 2. Click **Create user** 3. Enter a user name (e.g., `notiflows-ses`) 4. Do **not** check "Provide user access to the AWS Management Console" — Notiflows only needs programmatic access 5. Click **Next** 6. Select **Attach policies directly** 7. Search for the policy you created (`NotiflowsSESSendEmail`) and check it 8. Click **Next**, then **Create user** ### Generate access keys 1. Click on the user you just created to open the user details 2. Go to the **Security credentials** tab 3. Scroll to **Access keys** and click **Create access key** 4. Select **Third-party service** as the use case 5. Check the acknowledgment and click **Next**, then **Create access key** 6. Copy the **Access Key ID** and **Secret Access Key** The secret access key is only shown once. Copy it now and store it securely. If you lose it, you'll need to create a new access key pair. ## Step 4: Choose a region SES is a regional service. The region you choose in Notiflows must match the region where your verified identities are configured. Pick the region closest to your users for lower latency. | Region | Location | |--------|----------| | `us-east-1` | N. Virginia | | `us-west-2` | Oregon | | `eu-west-1` | Ireland | | `eu-central-1` | Frankfurt | | `ap-southeast-1` | Singapore | | `ap-southeast-2` | Sydney | ## Step 5: Set up in Notiflows 1. Navigate to **Channels** in your project 2. Click **Create Channel** 3. Select **Email** as the channel type 4. Select **Amazon SES** as the provider 5. Select your **Region** 6. Enter your IAM **Access Key ID** and **Secret Access Key** 7. Enter your verified **From Email** and optionally a **From Name** 8. Save the channel ## Templates Email templates support three content types: - **Visual** - Drag-and-drop editor for rich HTML emails - **HTML** - Raw HTML with full control - **Plaintext** - Simple text emails Use Liquid templating for dynamic content: ```liquid Hi {{ recipient.first_name }}, {{ data.message }} Best, {{ actor.first_name }} ``` Available variable contexts: - `recipient.*` - Recipient user data (first_name, last_name, email, etc.) - `actor.*` - User who triggered the notification - `data.*` - Custom payload passed when triggering the notiflow --- # SMTP > Configure any email provider via SMTP in Notiflows Source: https://notiflows.com/docs/channels-providers/email/smtp --- title: SMTP description: Configure any email provider via SMTP in Notiflows --- SMTP is a universal adapter that lets you connect any email provider that supports standard SMTP credentials. Use this when your provider isn't natively supported, or when you want to use your own mail server. ## Prerequisites Before configuring SMTP in Notiflows, you need: 1. SMTP server credentials from your email provider 2. A verified sender address (if required by your provider) ## Configuration | Field | Required | Description | |-------|----------|-------------| | Host | Yes | SMTP server hostname (e.g., `smtp.example.com`) | | Port | Yes | SMTP port (typically 587 for TLS, 465 for SSL, 25 for unencrypted) | | Username | No | SMTP authentication username | | Password | No | SMTP authentication password | | SSL | No | Enable SSL connection (default: off) | | TLS | No | TLS mode: `always`, `never`, or `if_available` (default) | | From Email | Yes | Sender email address | | From Name | No | Display name shown to recipients | ## Common SMTP Settings | Provider | Host | Port | TLS | |----------|------|------|-----| | Gmail | `smtp.gmail.com` | 587 | Always | | Outlook/Office 365 | `smtp.office365.com` | 587 | Always | | Postmark | `smtp.postmarkapp.com` | 587 | Always | | Mailgun | `smtp.mailgun.org` | 587 | Always | | SendGrid | `smtp.sendgrid.net` | 587 | Always | | Amazon SES | `email-smtp.{region}.amazonaws.com` | 587 | Always | ## Set up in Notiflows 1. Navigate to **Channels** in your project 2. Click **Create Channel** 3. Select **Email** as the channel type 4. Select **SMTP** as the provider 5. Enter your **Host**, **Port**, and authentication credentials 6. Configure **SSL/TLS** based on your provider's requirements 7. Enter your **From Email** and optionally a **From Name** 8. Save the channel ## Templates Email templates support three content types: - **Visual** - Drag-and-drop editor for rich HTML emails - **HTML** - Raw HTML with full control - **Plaintext** - Simple text emails Use Liquid templating for dynamic content: ```liquid Hi {{ recipient.first_name }}, {{ data.message }} ``` Available variable contexts: - `recipient.*` - Recipient user data (first_name, last_name, email, etc.) - `actor.*` - User who triggered the notification - `data.*` - Custom payload passed when triggering the notiflow --- # Overview > Display notifications directly within your application Source: https://notiflows.com/docs/channels-providers/in-app --- title: Overview description: Display notifications directly within your application --- In-app notifications are messages displayed directly within your application's UI using the Notiflows client SDKs. ## Configuration To create an in-app channel, configure the following: | Field | Required | Description | |-------|----------|-------------| | Retention Period | No | Number of days to retain notifications | ## Setup in Notiflows 1. Navigate to **Channels** in your project 2. Click **Create Channel** 3. Select **In-App** as the channel type 4. Select **Notiflows In-App** as the provider 5. Optionally set a retention period 6. Save the channel ## Client Integration ### React SDK Install the React SDK for pre-built components: ```bash npm install @notiflows/react ``` ```tsx import { NotiflowsProvider, FeedRoot, FeedTrigger, FeedContent, FeedPanel, } from '@notiflows/react'; function App() { return ( ); } ``` See the [React SDK documentation](/docs/sdks/client-side/react) for full component reference. ### JavaScript SDK For custom UI implementations: ```bash npm install @notiflows/client ``` ```typescript import { Notiflows } from '@notiflows/client'; const client = new Notiflows({ apiKey: 'your-public-api-key', userKey: 'user-key-from-backend', userId: 'user_123', }); const feed = client.feed({ channelId: 'your-in-app-channel-id' }); // Fetch notifications const { items, page } = await feed.getEntries(); // Subscribe to real-time updates feed.subscribeToRealtimeNotifications(); feed.onDelivery((entry) => { console.log('New notification:', entry); }); ``` See the [JavaScript SDK documentation](/docs/sdks/client-side/javascript) for full API reference. ## Templates In-app templates use markdown and support three action types for controlling how users interact with notifications. ### Action Types | Type | Value | Description | |------|-------|-------------| | Default | `default` | The entire notification is clickable. Navigates to the action URL when clicked. | | Single Action | `single` | Displays a single action button with a custom label. | | Multi Action | `multi` | Displays two action buttons — a primary and a secondary — each with its own label and URL. | ### Template Fields | Field | Action Type | Description | |-------|-------------|-------------| | **Body** | All | The notification message (supports Markdown) | | **Action Type** | All | One of `default`, `single`, or `multi` | | **Action URL** | `default` | URL to navigate when the notification is clicked | | **Primary Action** | `single`, `multi` | Object with `label` and `url` for the primary button | | **Secondary Action** | `multi` | Object with `label` and `url` for the secondary button | All action fields are optional. A notification without any action configured will render as static content with no clickable behavior. ### Liquid Templating Use Liquid syntax for dynamic content in any text field, including action URLs and button labels: ```liquid {{ actor.first_name }} commented on your post: "{{ data.comment_preview }}" ``` Available variable contexts: - `recipient.*` - Recipient user data (first_name, last_name, email, etc.) - `actor.*` - User who triggered the notification - `data.*` - Custom payload passed when triggering the notiflow --- # Notiflows In-App > Built-in in-app notification center Source: https://notiflows.com/docs/channels-providers/in-app/notiflows-in-app --- title: Notiflows In-App description: Built-in in-app notification center --- ## Notiflows In-App The Notiflows In-App channel provides a real-time, interactive notification center that integrates directly into your application. Unlike other channels, this is a built-in feature that doesn't require external provider configuration. ### What is the In-App Channel? The In-App channel delivers notifications directly within your application's interface. These notifications appear in a dedicated notification center, feed, or as toast messages, keeping users informed without leaving your app. ### Key Features **Real-Time Delivery** Notifications are delivered instantly through a real-time connection. Users see new notifications the moment they're triggered, without needing to refresh the page. **Pre-Built UI Components** Notiflows provides ready-to-use UI components that you can drop into your application: - **Notification Feed**: A scrollable list of all notifications - **Notification Inbox**: A full-featured inbox with tabs, filters, and actions - **Toast Notifications**: Temporary pop-up notifications for immediate attention - **Badge Counts**: Unread notification counters for navigation elements **Headless API** For complete customization, use our headless API to build your own notification UI. Fetch notifications, manage read states, and handle user interactions with full control over the presentation. **Message Status Management** Each in-app notification has a status that tracks user interaction: - **Unseen**: The notification has been delivered but not yet viewed - **Seen**: The notification has appeared in the user's view - **Read**: The user has explicitly marked the notification as read or clicked on it These statuses enable features like unread counters, filtering, and mark-all-as-read functionality. **Archiving** Users can archive notifications to remove them from their active feed while preserving them for later reference. This keeps the notification center clean and focused on relevant items. **Multi-Device Sync** Notification status and actions sync across all devices. If a user reads a notification on their phone, it appears as read on their desktop too. ### Setting Up the In-App Channel Since the In-App channel is built into Notiflows, setup is straightforward: 1. **Create an In-App Channel**: In your Notiflows dashboard, add an In-App channel 2. **Configure Your Notiflow**: Add an In-App step to your notiflow to send notifications through this channel 3. **Integrate the UI**: Add the Notiflows UI components to your application ### Integrating the Notification Inbox To add a notification inbox to your React application: **Prerequisites** - A Notiflows public API key - An In-App channel configured in your dashboard - A notiflow that sends In-App notifications **Installation** ```bash npm install @notiflows/react ``` **Basic Implementation** ```jsx import { NotiflowsProvider, NotificationInbox } from '@notiflows/react'; function App() { return ( ); } ``` The `NotiflowsProvider` handles authentication and real-time connections, while `NotificationInbox` renders the notification UI. ### Customization Options **Theming** Customize colors, fonts, and spacing to match your application's design: ```jsx ``` **Custom Rendering** For complete control, use the headless hooks to build custom UIs: ```jsx import { useNotifications } from '@notiflows/react'; function CustomInbox() { const { notifications, markAsRead, markAllAsRead } = useNotifications(); return (
{notifications.map(notification => (
markAsRead(notification.id)}> {notification.content}
))}
); } ``` ### Notification Content In-App notifications support rich content: - **Body**: Notification text with Markdown support - **Avatar**: Actor image derived from user data - **Action Types**: Three interaction modes: - **Default**: The entire notification is a clickable link - **Single Action**: A single action button with a custom label and URL - **Multi Action**: Two buttons (primary and secondary) each with their own label and URL - **Custom Data**: Additional metadata passed via Liquid template variables ### User Preferences Users can manage their In-App notification preferences: - Enable or disable specific notification types - Control which notiflows send in-app notifications ### Best Practices **Keep Notifications Relevant** Only send In-App notifications for information users actually need. Over-notification leads to users ignoring the notification center entirely. **Use Clear, Actionable Content** Write notification text that tells users what happened and what they can do about it. Include action buttons when appropriate. **Respect User Attention** Use toasts sparingly for truly time-sensitive information. Most notifications should appear in the feed without interrupting the user. **Design for Skimming** Users often scan their notification feed quickly. Use clear titles and concise descriptions that communicate value at a glance. **Test the Experience** Verify how notifications appear on different screen sizes and in different application states. The In-App channel provides a powerful way to keep users engaged within your application while respecting their attention and preferences. --- # Overview > Send push notifications to mobile devices Source: https://notiflows.com/docs/channels-providers/mobile-push --- title: Overview description: Send push notifications to mobile devices --- Mobile Push is a channel type for sending native push notifications to iOS and Android devices. ## Supported Providers Push notifications for iOS, iPadOS, macOS, and watchOS Cross-platform push for Android, iOS, and web ## Templates Mobile push templates use plaintext with Liquid templating for dynamic content. ### Content fields | Field | Liquid | Description | |-------|--------|-------------| | **Title** | Yes | Notification title (required) | | **Body** | Yes | Notification body text (required) | | **Subtitle** | Yes | Secondary text below title (iOS only) | | **Image URL** | Yes | Rich media image. Requires a Notification Service Extension on iOS | | **Action URL** | Yes | Deep link or URL opened on tap | ### Behavior fields | Field | Liquid | Description | |-------|--------|-------------| | **Sound** | Yes | `"default"` for system sound, or a custom sound filename | | **Badge Count** | No | App icon badge number. `0` clears it | | **Category** | Yes | Action button category (iOS `UNNotificationCategory` / Android `click_action`) | | **Thread ID** | Yes | Groups related notifications together | | **Collapse ID** | Yes | Replaces pending undelivered notifications with the same ID | | **Interruption Level** | No | iOS Focus mode behavior: passive, active, time-sensitive, critical | | **Priority** | No | Delivery urgency: high (immediate) or normal (battery-conscious) | | **Relevance Score** | No | 0.0–1.0 ranking in iOS Notification Summary | | **TTL** | No | Seconds to retry delivery if device is offline. `0` = deliver now or drop | | **Android Channel ID** | Yes | Android 8+ notification channel (must be pre-created in the app) | Leave behavior fields empty to use the provider's default. Only set them when you need to override. ### Liquid example ```liquid Title: New message from {{ actor.first_name }} Body: {{ data.message_preview }} Thread ID: {{ data.conversation_id }} Collapse ID: {{ data.conversation_id }} ``` ### Available variables - `recipient.*` - Recipient user data (first_name, last_name, email, phone, etc.) - `actor.*` - User who triggered the notification - `data.*` - Custom payload passed when triggering the notiflow --- # Apple APNs > Configure Apple Push Notification service in Notiflows Source: https://notiflows.com/docs/channels-providers/mobile-push/apns --- title: Apple APNs description: Configure Apple Push Notification service in Notiflows --- Apple Push Notification service (APNs) delivers push notifications to iOS, iPadOS, macOS, watchOS, and tvOS devices. ## Prerequisites Before configuring APNs in Notiflows, you need: 1. An [Apple Developer](https://developer.apple.com) account 2. An App ID with Push Notifications capability enabled 3. Either an APNs authentication key (`.p8` file) or a push certificate — see [Authentication methods](#authentication-methods) below ## Configuration | Field | Required | Description | |-------|----------|-------------| | Bundle ID | Yes | Your app's bundle identifier (e.g., `com.example.myapp`) | | Environment | Yes | **Production** for App Store builds, **Sandbox** for development | | Auth Type | Yes | **Key** (recommended) or **Certificate** | ## Authentication methods APNs supports two authentication methods. You only need one. ### Token-based authentication (recommended) Token-based authentication uses a signing key that Apple issues once per account. A single key works across all your apps and doesn't expire, making it the simpler option for most teams. | Field | Required | Description | |-------|----------|-------------| | Key ID | Yes | 10-character identifier shown when you download the key | | Team ID | Yes | 10-character identifier found in your Apple Developer account membership | | Key | Yes | Contents of your `.p8` key file | **How to get your signing key:** 1. Go to [Certificates, Identifiers & Profiles](https://developer.apple.com/account/resources) in your Apple Developer account 2. Navigate to **Keys** and click the **+** button 3. Name the key, check **Apple Push Notifications service (APNs)**, and click **Continue** 4. Click **Register**, then **Download** the `.p8` file 5. Note the **Key ID** displayed on the confirmation page Apple only lets you download the `.p8` file once. Store it securely. If you lose it, you'll need to revoke the key and create a new one. Open the `.p8` file in a text editor and paste the full contents (including the `-----BEGIN PRIVATE KEY-----` and `-----END PRIVATE KEY-----` lines) into the **Key** field in Notiflows. For more details, see Apple's guide on [establishing a token-based connection to APNs](https://developer.apple.com/documentation/usernotifications/establishing-a-token-based-connection-to-apns). ### Certificate-based authentication Certificate-based authentication uses a TLS client certificate specific to a single app. Certificates expire after one year and must be renewed. | Field | Required | Description | |-------|----------|-------------| | Certificate | Yes | X.509 certificate in PEM format | | Private Key | Yes | Unencrypted private key in PEM format | **How to get your push certificate:** 1. Go to [Certificates, Identifiers & Profiles](https://developer.apple.com/account/resources) in your Apple Developer account 2. Navigate to **Identifiers**, select your App ID, and enable **Push Notifications** 3. Click **Configure** and create a certificate for your environment (Development or Production) 4. Follow the prompts to upload a Certificate Signing Request (CSR) and download the `.cer` file 5. Open the `.cer` file in Keychain Access, then export it as a `.p12` file For more details, see Apple's guide on [establishing a certificate-based connection to APNs](https://developer.apple.com/documentation/usernotifications/establishing-a-certificate-based-connection-to-apns). **Converting your `.p12` to PEM format:** Notiflows expects PEM-formatted strings. Use the following commands to convert your `.p12` file: Extract the certificate: ```bash openssl pkcs12 -clcerts -nokeys -out cert.pem -in cert.p12 ``` Extract the private key (you'll be prompted to set a passphrase): ```bash openssl pkcs12 -nocerts -out key.pem -in cert.p12 ``` Remove the passphrase from the private key: ```bash openssl rsa -in key.pem -out key_unencrypted.pem ``` Paste the contents of `cert.pem` into the **Certificate** field and `key_unencrypted.pem` into the **Private Key** field in Notiflows. Push certificates are tied to a specific app and environment. You'll need separate certificates for development and production. Token-based authentication avoids this limitation — one key covers all apps and both environments. ## Setup in Notiflows 1. Navigate to **Channels** in your project 2. Click **Create Channel** 3. Select **Mobile Push** as the channel type 4. Select **Apple APNs** as the provider 5. Enter your **Bundle ID** 6. Select the **Environment** (Production or Sandbox) 7. Choose your **Auth Type** and enter the corresponding credentials 8. Save the channel ## Syncing device tokens To deliver push notifications, Notiflows needs the APNs device token for each user. Your app receives this token from Apple at runtime and must sync it to Notiflows via the User API. Set the device token as a channel setting on the user: ```bash curl -X PUT https://api.notiflows.com/user/v1/channel-settings \ -H "Authorization: Bearer USER_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "channel_id": "YOUR_APNS_CHANNEL_ID", "settings": { "device_tokens": ["DEVICE_TOKEN_FROM_APPLE"] } }' ``` A user can have multiple device tokens (one per device). Notiflows sends the notification to the first token in the list. ## Payload When Notiflows delivers a push notification via APNs, the payload includes all configured fields. Only non-empty template fields are included. ```json { "aps": { "alert": { "title": "Rendered title", "body": "Rendered body", "subtitle": "Rendered subtitle" }, "sound": "default", "badge": 3, "category": "MESSAGE_REPLY", "thread-id": "conv_456", "mutable-content": 1, "interruption-level": "time-sensitive", "relevance-score": 0.8 }, "notiflows_notification_id": "notif_abc123", "notiflows_delivery_id": "del_xyz789", "notiflows_image_url": "https://cdn.example.com/image.png", "notiflows_action_url": "https://app.com/orders/123", "notiflows_data": {} } ``` The `mutable-content` flag is set automatically when an image URL is configured, enabling your [Notification Service Extension](https://developer.apple.com/documentation/usernotifications/unnotificationserviceextension) to download and attach the image. APNs headers are also set when applicable: | Header | When set | |--------|----------| | `apns-collapse-id` | When Collapse ID is configured | | `apns-priority` | `5` when Priority is set to normal | | `apns-expiration` | Computed from TTL (current time + TTL seconds) | ## APNs-specific template fields These fields are only shown when the channel provider is APNs: | Field | Description | |-------|-------------| | **Subtitle** | Secondary text between title and body | | **Interruption Level** | Controls [Focus mode](https://developer.apple.com/documentation/usernotifications/unnotificationinterruptionlevel) behavior | | **Relevance Score** | Ranking in [Notification Summary](https://developer.apple.com/documentation/usernotifications/unnotificationcontent/relevancescore) (0.0–1.0) | See the [Mobile Push overview](/docs/channels-providers/mobile-push) for all shared template fields. --- # Firebase Cloud Messaging > Configure Firebase Cloud Messaging in Notiflows Source: https://notiflows.com/docs/channels-providers/mobile-push/fcm --- title: Firebase Cloud Messaging description: Configure Firebase Cloud Messaging in Notiflows --- Firebase Cloud Messaging (FCM) delivers push notifications to Android, iOS, and web applications using the FCM v1 API. ## Prerequisites Before configuring FCM in Notiflows, you need: 1. A [Firebase](https://console.firebase.google.com) project 2. A service account with the **Firebase Cloud Messaging API** enabled 3. A service account JSON key file ## Configuration | Field | Required | Description | |-------|----------|-------------| | Project ID | Yes | Your Firebase project ID (e.g., `my-app-12345`) | | Service Account JSON | Yes | Full contents of your service account key file | ## Generating service account credentials Notiflows authenticates with FCM using a Google service account. The service account JSON contains the credentials needed to request OAuth2 tokens for the FCM v1 API. 1. Go to the [Firebase Console](https://console.firebase.google.com) and select your project 2. Click the gear icon next to **Project Overview** and select **Project settings** 3. Go to the **Service accounts** tab 4. Click **Generate new private key**, then confirm 5. A JSON file downloads automatically — store it securely The service account JSON contains a private key. Treat it like a password. Do not commit it to version control or expose it in client-side code. The downloaded JSON looks like this: ```json { "type": "service_account", "project_id": "my-app-12345", "private_key_id": "key123...", "private_key": "-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----\n", "client_email": "firebase-adminsdk-abc@my-app-12345.iam.gserviceaccount.com", "client_id": "123456789", "auth_uri": "https://accounts.google.com/o/oauth2/auth", "token_uri": "https://oauth2.googleapis.com/token", "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/..." } ``` Paste the **entire JSON contents** into the **Service Account JSON** field in Notiflows. The **Project ID** field should match the `project_id` value in the JSON. ### Enabling the FCM API If you created your Firebase project recently, the FCM v1 API should be enabled by default. If not: 1. Go to the [Google Cloud Console](https://console.cloud.google.com) 2. Select your Firebase project 3. Navigate to **APIs & Services** > **Enabled APIs** 4. Search for **Firebase Cloud Messaging API** and enable it ## Setup in Notiflows 1. Navigate to **Channels** in your project 2. Click **Create Channel** 3. Select **Mobile Push** as the channel type 4. Select **Firebase Cloud Messaging** as the provider 5. Enter your **Project ID** 6. Paste the full contents of your service account JSON 7. Save the channel ## Syncing device tokens To deliver push notifications, Notiflows needs the FCM registration token for each user. Your app receives this token from the Firebase SDK at runtime and must sync it to Notiflows via the User API. Set the device token as a channel setting on the user: ```bash curl -X PUT https://api.notiflows.com/user/v1/channel-settings \ -H "Authorization: Bearer USER_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "channel_id": "YOUR_FCM_CHANNEL_ID", "settings": { "device_tokens": ["FCM_REGISTRATION_TOKEN"] } }' ``` A user can have multiple device tokens (one per device). Notiflows sends the notification to the first token in the list. FCM registration tokens can change over time. Your app should listen for token refresh events and sync the new token to Notiflows. See the Firebase documentation for [token management](https://firebase.google.com/docs/cloud-messaging/manage-tokens) on each platform. ## Payload When Notiflows delivers a push notification via FCM, the payload includes a notification message, a data message, and platform-specific overrides. Only non-empty template fields are included. ```json { "message": { "notification": { "title": "Rendered title", "body": "Rendered body", "image": "https://cdn.example.com/image.png" }, "data": { "notiflowsNotificationId": "notif_abc123", "notiflowsDeliveryId": "del_xyz789", "notiflowsActionUrl": "https://app.com/orders/123", "notiflowsData": "{...}" }, "android": { "priority": "NORMAL", "collapse_key": "order_123", "ttl": "3600s", "notification": { "sound": "default", "channel_id": "orders", "click_action": "ORDER_UPDATE", "tag": "order_123", "notification_count": 3 } }, "apns": { "headers": { "apns-collapse-id": "order_123", "apns-priority": "5" }, "payload": { "aps": { "sound": "default", "badge": 3, "category": "ORDER_UPDATE", "thread-id": "conv_456", "mutable-content": 1, "interruption-level": "time-sensitive", "relevance-score": 0.8 } } }, "token": "device_registration_token" } } ``` The `data` fields are always delivered as strings. `notiflowsData` is a JSON-encoded string containing the full delivery data. FCM automatically includes an `apns` override block so iOS devices receiving via FCM get the full native APNs experience (action buttons, Focus mode, Notification Summary ranking, etc.). ## FCM-specific template fields This field is only shown when the channel provider is FCM: | Field | Description | |-------|-------------| | **Android Channel ID** | Android 8+ [notification channel](https://developer.android.com/develop/ui/views/notifications#ManageChannels). Must be created in your app before use. | See the [Mobile Push overview](/docs/channels-providers/mobile-push) for all shared template fields. --- # Overview > Send SMS notifications Source: https://notiflows.com/docs/channels-providers/sms --- title: Overview description: Send SMS notifications --- SMS is a channel type for sending text messages to mobile phones. ## Supported Providers Global SMS delivery platform Leading Polish SMS provider (smsapi.pl / .com) ## Templates SMS templates use plaintext content type. Use Liquid templating for dynamic content: ```liquid Hi {{ recipient.first_name }}, your order #{{ data.order_id }} has shipped! ``` Available variable contexts: - `recipient.*` - Recipient user data (first_name, last_name, phone, etc.) - `actor.*` - User who triggered the notification - `data.*` - Custom payload passed when triggering the notiflow ## Phone Number Format Store recipient phone numbers in E.164 format: ``` +15551234567 (US) +447911123456 (UK) ``` --- # SMSAPI > Configure SMSAPI as an SMS provider in Notiflows Source: https://notiflows.com/docs/channels-providers/sms/smsapi --- title: SMSAPI description: Configure SMSAPI as an SMS provider in Notiflows --- [SMSAPI](https://www.smsapi.pl/) is the leading Polish SMS provider. It runs two independent platforms — **SMSAPI.pl** for Poland and **SMSAPI.com** for international traffic — each with its own account and access token. Notiflows delivers with UTF-8 encoding so Polish characters render correctly. ## Prerequisites Before configuring SMSAPI in Notiflows, you need: 1. An [SMSAPI account](https://www.smsapi.pl/) on the platform you plan to use (`.pl` for Poland or `.com` for international) 2. An OAuth2 **API access token** generated in your SMSAPI dashboard 3. A **sender name** that has been registered and verified in SMSAPI ## Configuration | Field | Required | Description | |-------|----------|-------------| | Access token | Yes | Your SMSAPI OAuth2 API access token (used as a Bearer token). Stored encrypted. | | Sender name | Yes | The verified sender name (`from`) shown to recipients. **Maximum 11 characters** and must be pre-registered in SMSAPI. | | Service | Yes | The SMSAPI platform to send through: **SMSAPI.pl** (Poland, default) or **SMSAPI.com** (international). | SMSAPI.pl and SMSAPI.com are separate platforms with separate accounts and tokens. Make sure the access token and sender name you enter belong to the same platform you select under **Service**. ## Step 1: Generate an API access token 1. Log in to your [SMSAPI dashboard](https://ssl.smsapi.pl/) (or the `.com` dashboard for international) 2. Open **Settings** > **API** 3. Create a new **OAuth2 access token** and grant it permission to send SMS 4. Copy the token — you'll paste it into Notiflows as the **Access token** Treat the access token like a password. Never share it, commit it to version control, or expose it in client-side code. ## Step 2: Register a sender name Polish operators require SMS to be sent from a **registered sender name** — you cannot send from an arbitrary string. 1. In the SMSAPI dashboard, open the **Sender names** section 2. Add the sender name you want recipients to see (for example, your brand name) 3. Submit it for verification and wait for approval 4. Note the exact approved value — it must be **11 characters or fewer** You'll enter this value in Notiflows as the **Sender name**. ## Step 3: Set up in Notiflows 1. Navigate to **Channels** in your project 2. Click **Create Channel** 3. Select **SMS** as the channel type 4. Select **SMSAPI** as the provider 5. Enter your **Access token** 6. Enter your verified **Sender name** (max 11 characters) 7. Select the **Service** — **SMSAPI.pl** (Poland) or **SMSAPI.com** (international) 8. Save the channel Under the hood Notiflows sends via SMSAPI's `POST /sms.do` HTTP API with UTF-8 encoding. ## Recipient phone numbers Notiflows sends SMS to the `phone` stored on the recipient user. Numbers can be in international E.164 format or the local Polish format: ``` +48512345678 (E.164) 512345678 (local Polish) ``` Set the phone number on a user via the Admin API: ```bash curl -X PUT https://api.notiflows.com/admin/v1/users/USER_ID \ -H "x-notiflows-api-key: API_KEY" \ -H "x-notiflows-secret-key: SECRET_KEY" \ -H "Content-Type: application/json" \ -d '{ "phone": "+48512345678" }' ``` ## Templates SMS templates use plaintext only. Because Notiflows sends with UTF-8 encoding, Polish characters (ą, ć, ę, ł, ń, ó, ś, ź, ż) are delivered as written. Use Liquid templating for dynamic content: ```liquid Cześć {{ recipient.first_name }}, Twoje zamówienie #{{ data.order_id }} zostało wysłane! ``` Available variable contexts: - `recipient.*` - Recipient user data (first_name, last_name, phone, etc.) - `actor.*` - User who triggered the notification - `data.*` - Custom payload passed when triggering the notiflow --- # Twilio > Configure Twilio as an SMS provider in Notiflows Source: https://notiflows.com/docs/channels-providers/sms/twilio --- title: Twilio description: Configure Twilio as an SMS provider in Notiflows --- Twilio is a global SMS delivery platform with carrier-grade reliability. ## Prerequisites Before configuring Twilio in Notiflows, you need: 1. A [Twilio account](https://www.twilio.com/try-twilio) 2. Your Account SID and Auth Token 3. A phone number, messaging service, or short code to send from ## Configuration | Field | Required | Description | |-------|----------|-------------| | Account SID | Yes | Your Twilio Account SID | | Auth Token | Yes | Your Twilio Auth Token | | From Type | Yes | How to identify the sender: **Phone Number**, **Messaging Service SID**, or **Short Code** | | Phone Number | Conditional | Sender phone number in E.164 format (e.g., `+15551234567`) | | Messaging Service SID | Conditional | Twilio Messaging Service identifier | | Short Code | Conditional | Your registered short code | You must provide one of Phone Number, Messaging Service SID, or Short Code based on your selected From Type. ## Step 1: Find your Account SID and Auth Token 1. Log in to the [Twilio Console](https://www.twilio.com/console) 2. On the dashboard, locate the **Account Info** section 3. Your **Account SID** is displayed directly — copy it 4. Your **Auth Token** is hidden by default — click **Show** to reveal it, then copy it Your Auth Token is effectively your account password. Never share it, commit it to version control, or expose it in client-side code. ## Step 2: Set up a sender You need something to send SMS from. Choose one of the three options below. ### Option A: Buy a phone number (simplest) 1. In the Twilio Console, click **Phone Numbers** > **Manage** > **Buy a number** in the left navigation 2. Select your country and search for available numbers 3. Ensure the number has **SMS** capability (check the SMS column) 4. Click **Buy** and confirm the purchase 5. Copy the phone number in E.164 format (e.g., `+15551234567`) ### Option B: Create a messaging service Messaging services let you manage a pool of sender phone numbers with automatic load balancing and failover. Recommended if you send high volumes or need multiple sender numbers. 1. In the Twilio Console, click **Messaging** > **Services** in the left navigation 2. Click **Create Messaging Service** 3. Enter a name (e.g., `Notiflows Notifications`) 4. Select the use case that best describes your traffic 5. Click **Create Messaging Service** 6. In the **Sender Pool** step, click **Add Senders** and add one or more phone numbers 7. Complete the setup and copy the **Messaging Service SID** (starts with `MG`) ### Option C: Use a short code If you have a registered short code, you can use it directly. Short codes are provisioned through Twilio's sales team and are typically used for high-volume marketing or two-factor authentication. ## Step 3: Set up in Notiflows 1. Navigate to **Channels** in your project 2. Click **Create Channel** 3. Select **SMS** as the channel type 4. Select **Twilio** as the provider 5. Enter your **Account SID** and **Auth Token** 6. Select your **From Type** and enter the corresponding value: - **Phone Number** — enter the number in E.164 format - **Messaging Service SID** — enter the SID starting with `MG` - **Short Code** — enter your short code 7. Save the channel ## Recipient phone numbers Notiflows sends SMS to the phone number stored on the recipient user. Phone numbers must be in E.164 format: ``` +15551234567 (US) +447911123456 (UK) +61412345678 (Australia) ``` Set the phone number on a user via the Admin API: ```bash curl -X PUT https://api.notiflows.com/admin/v1/users/USER_ID \ -H "Authorization: Bearer API_KEY" \ -H "Content-Type: application/json" \ -d '{ "phone_number": "+15551234567" }' ``` ## Templates SMS templates use plaintext only. Use Liquid templating for dynamic content: ```liquid Hi {{ recipient.first_name }}, your order #{{ data.order_id }} has shipped! ``` Available variable contexts: - `recipient.*` - Recipient user data (first_name, last_name, phone_number, etc.) - `actor.*` - User who triggered the notification - `data.*` - Custom payload passed when triggering the notiflow --- # Overview > Web push notifications using the Web Push standard (VAPID) Source: https://notiflows.com/docs/channels-providers/web-push --- title: Overview description: Web push notifications using the Web Push standard (VAPID) --- Web Push enables you to send push notifications directly to web browsers using the [Web Push Protocol](https://datatracker.ietf.org/doc/html/rfc8030) with [VAPID](https://datatracker.ietf.org/doc/html/rfc8292) authentication, even when users aren't actively on your site. ## Supported Browsers - Chrome - Firefox - Safari - Edge ## Setup ### 1. Generate VAPID Keys Generate a VAPID key pair for your application. You can use any standard tool: ```bash npx web-push generate-vapid-keys ``` This produces a public key and a private key. Keep the private key secret. ### 2. Create a Web Push Channel In your Notiflows dashboard, go to **Channels** and create a new **Web Push** channel with the **Web Push (VAPID)** provider. You'll need to provide: | Field | Description | |-------|-------------| | VAPID Public Key | The base64url-encoded public key from step 1 | | VAPID Private Key | The base64url-encoded private key from step 1 | | VAPID Subject | A contact URI — either `mailto:admin@yourapp.com` or `https://yourapp.com` | The VAPID subject identifies your application server to push services. It must be a valid `mailto:` address or `https://` URL. ### 3. Add a Service Worker Your application needs a service worker to handle incoming push events and display notifications. Create a `sw.js` file at the root of your site: ```javascript self.addEventListener('push', (event) => { const data = event.data?.json() ?? {}; const title = data.title || 'New Notification'; const options = { body: data.body || '', icon: data.icon, image: data.image, data: { actionUrl: data.action_url }, }; event.waitUntil(self.registration.showNotification(title, options)); }); self.addEventListener('notificationclick', (event) => { event.notification.close(); const actionUrl = event.notification.data?.actionUrl; if (actionUrl) { event.waitUntil(clients.openWindow(actionUrl)); } }); ``` ### 4. Register Push Subscriptions On the client side, use the browser's Push API to subscribe users and send the subscription to Notiflows via the [Channel Subscriptions](/docs/concepts/channel-subscriptions) API: ```javascript // Register the service worker const registration = await navigator.serviceWorker.register('/sw.js'); await navigator.serviceWorker.ready; // Request permission and subscribe const subscription = await registration.pushManager.subscribe({ userVisibleOnly: true, applicationServerKey: '', }); // Send the subscription to Notiflows via the User API const { endpoint, keys } = subscription.toJSON(); await fetch('https://api.notiflows.com/user/v1/channels//subscriptions', { method: 'POST', headers: { 'Content-Type': 'application/json', 'x-notiflows-api-key': '', 'x-notiflows-user-key': '', }, body: JSON.stringify({ identifier: endpoint, settings: { endpoint, p256dh: keys.p256dh, auth: keys.auth, }, }), }); ``` The `applicationServerKey` must be the same VAPID public key configured on your channel. Users can subscribe from multiple browsers or devices. Notiflows deduplicates by `identifier` and delivers to all registered subscriptions. It is safe to call this on every page load — if the subscription already exists, the settings are updated in place. ### 5. Handle Unsubscription When a user opts out of push notifications, unsubscribe them from the browser and remove the subscription from Notiflows: ```javascript const registration = await navigator.serviceWorker.ready; const subscription = await registration.pushManager.getSubscription(); if (subscription) { const { endpoint } = subscription.toJSON(); // Remove from Notiflows await fetch('https://api.notiflows.com/user/v1/channels//subscriptions', { method: 'DELETE', headers: { 'Content-Type': 'application/json', 'x-notiflows-api-key': '', 'x-notiflows-user-key': '', }, body: JSON.stringify({ identifier: endpoint }), }); // Unsubscribe the browser await subscription.unsubscribe(); } ``` ## Templates Web push templates support the following fields: | Field | Required | Format | Description | |-------|----------|--------|-------------| | **Title** | Yes | Plaintext | The notification heading | | **Body** | Yes | Plaintext | The notification body text | | **Icon URL** | No | URL | Small image displayed next to the title (typically your app logo) | | **Image URL** | No | URL | Large image displayed in the notification body | | **Action URL** | No | URL | URL opened when the user clicks the notification | Title and body are always plaintext — this is a browser limitation. All fields support [Liquid templating](/docs/learn/building-notiflows/templates) for dynamic content. **Example:** | Field | Value | |-------|-------| | Title | `New message from {{ actor.first_name }}` | | Body | `{{ data.message_preview }}` | | Icon URL | `https://yourapp.com/icon.png` | | Action URL | `https://yourapp.com/messages/{{ data.message_id }}` | --- # Overview > Deliver notifications to any HTTP endpoint Source: https://notiflows.com/docs/channels-providers/webhooks --- title: Overview description: Deliver notifications to any HTTP endpoint --- Webhooks allow you to send notifications to any external system via HTTP requests. Use webhooks to trigger automation workflows, sync with CRM systems, or integrate with any service that accepts HTTP callbacks. ## Channel Configuration The webhook channel only requires signing configuration. URL, method, headers, and body are configured per-step in the notiflow template editor. | Field | Required | Description | |-------|----------|-------------| | Request Signing | No | Enable HMAC-SHA256 request signing | | Signing Key | When signing is enabled | Secret key used to sign request payloads | ## Setup in Notiflows 1. Navigate to **Channels** in your project 2. Click **Create Channel** 3. Select **Webhook** as the channel type 4. Optionally enable request signing and enter a signing key 5. Save the channel ## Step Template Each webhook step in a notiflow defines the request details: | Field | Required | Description | |-------|----------|-------------| | URL | Yes | The endpoint URL. Supports Liquid: `{{ recipient.external_id }}` | | Method | Yes | HTTP method: GET, POST, PUT, PATCH, or DELETE | | Headers | No | Custom headers as key-value pairs or JSON. Supports Liquid. | | Body | No | Request body as key-value pairs or JSON. Supports Liquid. | ## Request Format When a webhook fires, Notiflows sends an HTTP request: - **Method**: As configured in the step template - **Content-Type**: `application/json` (unless overridden in headers) - **Body**: The compiled step template body, or a default payload ### Default Payload If no body is configured, the request includes a default JSON payload: ```json { "event": "notification.delivered", "delivery_id": "01HQ...", "notification_id": "01HQ...", "recipient_id": "01HQ...", "timestamp": "2026-03-19T12:00:00Z" } ``` ## Liquid Templating All template fields (URL, headers, body) support Liquid variables: ```json { "user_id": "{{ recipient.external_id }}", "event": "order_shipped", "data": { "order_id": "{{ data.order_id }}", "tracking_url": "{{ data.tracking_url }}" } } ``` Available variable contexts: - `recipient.*` — Recipient user data (external_id, email, first_name, etc.) - `actor.*` — User who triggered the notification - `data.*` — Custom payload passed when triggering the notiflow ## Request Signing When signing is enabled on the channel, Notiflows includes an `x-webhook-signature` header on every request. This allows you to verify that requests are genuinely from Notiflows. ### Signature Format ``` x-webhook-signature: t=1700000000,s=a1b2c3d4... ``` - `t` — Unix timestamp (seconds) when the signature was generated - `s` — HMAC-SHA256 hex digest of `{timestamp}.{request_body}` using your signing key ### Verifying Signatures 1. Extract `t` (timestamp) and `s` (signature) from the `x-webhook-signature` header 2. Reconstruct the signed payload: `{t}.{raw_request_body}` 3. Compute HMAC-SHA256 using your signing key 4. Compare your computed signature with `s` (must match exactly) 5. Optionally reject requests where `t` is more than 5 minutes old **Example (Node.js):** ```javascript const crypto = require('crypto'); function verifyWebhook(signingKey, header, body) { const [tPart, sPart] = header.split(','); const timestamp = tPart.replace('t=', ''); const signature = sPart.replace('s=', ''); const expected = crypto .createHmac('sha256', signingKey) .update(`${timestamp}.${body}`) .digest('hex'); return crypto.timingSafeEqual( Buffer.from(signature), Buffer.from(expected) ); } ``` ## Use Cases - Trigger automation workflows (Zapier, Make, n8n) - Sync with CRM or helpdesk systems - Send to custom internal microservices - Integrate with third-party APIs - Fan out to multiple downstream systems --- # CLI Overview > Install the Notiflows CLI, authenticate with an account token, and manage your notiflows as code — pull, edit, push, and publish from the terminal or CI. Source: https://notiflows.com/docs/cli --- title: CLI Overview description: Install the Notiflows CLI, authenticate with an account token, and manage your notiflows as code — pull, edit, push, and publish from the terminal or CI. --- The Notiflows CLI (`@notiflows/cli`) is a **notiflows-as-code** tool. It pulls the notiflows in a project into version-controllable local files, lets you edit them in your editor, and pushes them back through the [Management API](/docs/api). It also wraps the full lifecycle (publish, rollback, activate, deactivate, archive), read-only inspection (list/get versions, channels, projects), and triggering runs. The CLI is a thin client over the Management API — creation, validation, versioning, and execution all happen server-side. The CLI only serializes notiflows to and from disk and calls the API. ## Install The package installs two binary aliases: `notiflows` and the shorthand `nf`. ```bash npm install -g @notiflows/cli # or run without installing npx @notiflows/cli --help ``` ## Authenticate The CLI authenticates with an **account token** (prefix `nf_at_`). Create one in the dashboard ([Account tokens](https://app.notiflows.com/account-tokens)), then log in: ```bash notiflows login # or pass it non-interactively notiflows login --token nf_at_xxx ``` The token is stored at `~/.config/notiflows/credentials.json` with mode `0600` (owner read/write only). **Resolution precedence.** The token is resolved as `--token` flag → `NOTIFLOWS_TOKEN` env → the stored credentials file. The project is resolved as `--project` flag → `NOTIFLOWS_PROJECT` env → the `project` field in `notiflows.json`. See [Account tokens](/docs/cli/account-tokens) for how to create, scope, and rotate the token. Confirm who you are and which project is active: ```bash notiflows whoami ``` ## Quickstart A typical loop is **init → pull → edit → push → publish**: ```bash # 1. Create notiflows.json + the .notiflows/notiflows/ working tree notiflows init my-app # 2. Pull every notiflow in the project into local files notiflows pull # 3. Edit notiflow.json / template body files in your editor, then # push your changes back (creates a new draft version) notiflows push # 4. Publish the draft so it becomes live for execution notiflows notiflow publish welcome-series ``` Notiflows live under `/notiflows//` (default `.notiflows`). Each notiflow is a `notiflow.json` plus extracted template body files — see [Notiflows as code](/docs/cli/notiflows-as-code) for the on-disk format. ## Explore The on-disk format: notiflows.json, per-notiflow notiflow.json, the flat steps array, and template body extraction. Every command, grouped by area — auth, project, sync, lifecycle, trigger, and channels. Run the CLI in continuous integration with environment-based auth and idempotent pushes. The same operations, conversationally, inside an AI editor. --- # Account tokens > An account token (prefix nf_at_) is the credential that authenticates every Notiflows developer surface — the CLI, the MCP server, the agent toolkit, and the Management API. Create one in the dashboard, use it, and rotate it safely. Source: https://notiflows.com/docs/cli/account-tokens --- title: Account tokens description: An account token (prefix nf_at_) is the credential that authenticates every Notiflows developer surface — the CLI, the MCP server, the agent toolkit, and the Management API. Create one in the dashboard, use it, and rotate it safely. --- An **account token** (prefix `nf_at_`) is the credential that authenticates the Notiflows developer surfaces: the [CLI](/docs/cli), the [MCP server](/docs/ai/mcp), the [agent toolkit](/docs/ai/agent-toolkit), and the [Management API](/docs/api/management) they all sit on top of. Account tokens are **not** the Admin or User API credential. Those server-to-server and client APIs authenticate with `x-notiflows-api-key` plus a secret key or user key — see the [API reference](/docs/api). Account tokens authenticate only the developer/AI surfaces over the Management API. ## Create one Tokens are created in the dashboard, alongside Profile, Billing, and Team in your account settings. 1. Open **Account tokens** at [`https://app.notiflows.com/account-tokens`](https://app.notiflows.com/account-tokens). 2. Click **New token** and give it a descriptive **name** (e.g. `CI/CD Pipeline`, `claude-mcp`, `github-ci`). 3. Copy the secret immediately — it is **shown once** and cannot be retrieved again. The token secret is displayed a single time at creation. If you lose it, you can't view it again — delete the token and create a new one. Store it in a secret manager or environment variable right away. ## Scope Account tokens are **account-scoped**: a single token can access **every project in the account**. There are **no per-project tokens and no fine-grained scopes** today — a token is broad, with full read/write access to everything the account owns. You select the *target project* per request rather than per token: - **CLI** — the `--project` flag, the `NOTIFLOWS_PROJECT` env var, or the `project` field in `notiflows.json`. - **MCP server** — the `project` argument on each tool call (call `list_projects` to discover slugs). - **Management API** — the project slug in the path (`/projects/{project_slug}/…`). ## Lifecycle Account tokens **do not expire**. From the dashboard you can: - **Rename** a token. - See its **last-used** time. - **Delete (revoke)** a token. There is no in-place rotation: **rotate by creating a new token and deleting the old one.** If a token ever leaks, revoke it immediately and issue a replacement. ## Where it's used The same token authorizes every developer surface — only the mechanism differs: ```bash # CLI — login stores it at ~/.config/notiflows/credentials.json notiflows login --token nf_at_xxx # or non-interactively via env / flag export NOTIFLOWS_TOKEN=nf_at_xxx notiflows whoami --token nf_at_xxx ``` ```bash # MCP server — sent as an Authorization: Bearer header to the hosted endpoint claude mcp add notiflows -- npx -y mcp-remote https://api.notiflows.com/mcp \ --header "Authorization: Bearer nf_at_xxx" ``` ```ts // Agent toolkit — the accountToken option import { createNotiflowsToolkit } from "@notiflows/agent-toolkit"; const toolkit = createNotiflowsToolkit({ accountToken: process.env.NOTIFLOWS_TOKEN!, // nf_at_... project: "acme", }); ``` ```bash # Management API — Authorization: Bearer curl https://api.notiflows.com/management/v1/whoami \ -H "Authorization: Bearer nf_at_xxx" ``` ## Auditability Actions taken with a token are attributed to it **by name**: - Notiflow runs in the dashboard show **"Triggered by <token name>"**. - Notiflow version history records the token as the **creator** of a change. Name tokens after the system that uses them (e.g. `github-ci`, `claude-mcp`) so the audit trail reads clearly. ## Security Treat an account token as a secret with **full account write access**: - Store it in a secret manager or environment variable; **never commit it** to a repository. - Scope it to one system and name it accordingly so a leak is easy to attribute and revoke. - Revoke immediately on any suspected leak, then issue a replacement. The [MCP server](/docs/ai/mcp) exposes no destructive (delete/archive) tools, but the token still carries **full write access** through the raw Management API. Guard it as a privileged credential regardless of which surface uses it. ## Related Install, log in with a token, and manage notiflows as code. Send the token as an Authorization: Bearer header from your AI editor. Pass the token as accountToken to createNotiflowsToolkit. Send the token as Authorization: Bearer on every request. --- # CI/CD > Run the Notiflows CLI in continuous integration — environment-based authentication, idempotent pushes, publishing, and a GitHub Actions example. Source: https://notiflows.com/docs/cli/ci-cd --- title: CI/CD description: Run the Notiflows CLI in continuous integration — environment-based authentication, idempotent pushes, publishing, and a GitHub Actions example. --- Because notiflows are stored as files in your repo, you can deploy them the same way you deploy code: commit changes, open a pull request, review the diff, and let CI push the merged result to Notiflows. ## Authenticate with environment variables In CI you don't run `notiflows login`. Instead, set the token and project as environment variables — the CLI resolves them automatically (`NOTIFLOWS_TOKEN` for auth, `NOTIFLOWS_PROJECT` for the target project): ```bash export NOTIFLOWS_TOKEN=nf_at_xxx # store as a CI secret export NOTIFLOWS_PROJECT=my-app # or keep it in notiflows.json ``` Store `NOTIFLOWS_TOKEN` as an encrypted secret in your CI provider. `NOTIFLOWS_PROJECT` can either be an env var or the `project` field in `notiflows.json`. Use a dedicated, descriptively named [account token](/docs/cli/account-tokens) for CI (e.g. `github-ci`) so its actions are attributable and easy to revoke. ## Push is idempotent `notiflows push` is safe to run on every build. The Management API discards a draft whose content matches the version it branched from, so **re-pushing unchanged content creates no new version** — only genuinely changed notiflows bump a version. That keeps a push-on-merge pipeline free of noise. Add `--publish` to publish in the same step so the merged notiflows go live: ```bash notiflows push --force --publish ``` Use `--force` in non-interactive environments to skip the confirmation prompt. (Be aware that `--force` also skips the conflict check; if you expect concurrent dashboard edits, push without `--force` and resolve conflicts by pulling first.) ## GitHub Actions A GitHub Actions workflow that pushes and publishes notiflows on merge to `main`: ```yaml name: Deploy notiflows on: push: branches: [main] jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: 20 - name: Install the Notiflows CLI run: npm install -g @notiflows/cli - name: Push and publish notiflows env: NOTIFLOWS_TOKEN: ${{ secrets.NOTIFLOWS_TOKEN }} NOTIFLOWS_PROJECT: my-app run: notiflows push --force --publish ``` To gate merges on validity, run `notiflows diff` (or `notiflow validate`) on pull requests and `notiflows push --publish` only on merge to your main branch. ## Related - [Command reference](/docs/cli/commands) — `push`, `diff`, `validate`, and `publish`. - [Notiflows as code](/docs/cli/notiflows-as-code) — the files your pipeline deploys. --- # Command reference > The complete Notiflows CLI command reference — authentication, project inspection, sync (pull/push/diff), notiflow lifecycle, triggering runs, and channels. Source: https://notiflows.com/docs/cli/commands --- title: Command reference description: The complete Notiflows CLI command reference — authentication, project inspection, sync (pull/push/diff), notiflow lifecycle, triggering runs, and channels. --- Every command is shown with the `notiflows` binary; the shorthand `nf` is equivalent (`nf notiflow push`). Authenticated commands resolve the account token and project using the [resolution precedence](/docs/cli#authenticate). Most commands accept `--json` for machine-readable output. **Context-aware single-notiflow commands.** `notiflow get`, `push`, `pull`, `validate`, `publish`, `open`, and `run` infer the target handle from your current directory when you're inside a `/notiflows//` folder, so you can omit the handle argument. ## Auth ### `login` Authenticate with an account token (prefix `nf_at_`). Prompts for the token, or pass `--token`. Stores it at `~/.config/notiflows/credentials.json` (mode `0600`). Optional `--base-url` overrides the API endpoint. ```bash notiflows login notiflows login --token nf_at_xxx ``` ### `logout` Remove the stored credentials. ```bash notiflows logout ``` ### `whoami` Confirm the authenticated account and the active project. ```bash notiflows whoami ``` ## Project ### `init [project]` Create `notiflows.json` and the `.notiflows/notiflows/` working tree in the current directory. The optional `project` argument writes the project slug into the config. ```bash notiflows init my-app ``` ### `project list` List the projects your account token can access (read-only). ```bash notiflows project list ``` ### `project get ` Show details for one project (read-only). ```bash notiflows project get my-app ``` ## Sync These commands operate on the whole local working tree. ### `pull` Pull every notiflow in the project into local files, overwriting after one confirmation. ```bash notiflows pull ``` ### `push [--publish]` Push all changed local notiflows back to the server (each push creates or updates a draft version). Flags: - `--publish` — publish each notiflow after pushing. - `--force` / `-f` — skip the confirmation prompt **and** the conflict check (overwrite remote state). ```bash notiflows push notiflows push --publish ``` **Idempotent and conflict-safe.** Re-pushing **unchanged** content creates no new version, so `push` is safe to run on every deploy. Each push also sends an `expected_sha` (the version you pulled); if the notiflow changed on the server since you pulled, the push is rejected as a conflict — pull and re-push, or use `--force` to overwrite. ### `diff [handle]` Show the differences between local notiflow files and the server. Omit the handle to diff all local notiflows; add `--json` for machine-readable output. ```bash notiflows diff notiflows diff welcome-series notiflows diff --json ``` ## Notiflow lifecycle Single-notiflow commands under the `notiflow` topic. ### `notiflow new [handle]` Scaffold a new notiflow with a minimal configuration. Flags: `--name`/`-n` (display name), `--steps`/`-s` (comma-separated step types, e.g. `email,wait,sms`), `--push`/`-p` (push after creation), `--force`/`-f`. ```bash notiflows notiflow new welcome-series --name "Welcome Series" --steps email,wait,email ``` ### `notiflow get [handle]` Fetch a notiflow with its current and published version steps. ```bash notiflows notiflow get welcome-series ``` ### `notiflow list` List the notiflows in the project (summaries, no steps). ```bash notiflows notiflow list ``` ### `notiflow pull [handle]` Pull a single notiflow into local files. Respects existing files unless `--force`; `--all` pulls everything and **prunes** local directories no longer present on the server (destructive). ```bash notiflows notiflow pull welcome-series ``` ### `notiflow push [handle]` Push a single notiflow (upsert draft). Add `--publish` to publish afterward; `--force` to overwrite on conflict. ```bash notiflows notiflow push welcome-series --publish ``` ### `notiflow publish [handle]` Publish the current draft version so it becomes live for execution. ```bash notiflows notiflow publish welcome-series ``` ### `notiflow rollback ` Discard unpublished draft changes and revert to the published version. ```bash notiflows notiflow rollback welcome-series ``` ### `notiflow activate ` / `notiflow deactivate ` Toggle whether a notiflow can be triggered (the active flag). ```bash notiflows notiflow activate welcome-series notiflows notiflow deactivate welcome-series ``` ### `notiflow archive ` Archive (delete) a notiflow. ```bash notiflows notiflow archive welcome-series ``` ### `notiflow versions ` List a notiflow's version history. ```bash notiflows notiflow versions welcome-series ``` ### `notiflow validate [handle]` Validate the notiflow's current draft on the server. Returns whether the current version is valid. ```bash notiflows notiflow validate welcome-series ``` ### `notiflow open [handle]` Open the notiflow in the dashboard in your browser. ```bash notiflows notiflow open welcome-series ``` ## Trigger ### `notiflow run [handle]` Trigger (run) a notiflow to send notifications. The run is **attributed to the calling account token** (it shows up as "Triggered by" the token in the dashboard). By **default the published version runs**, so the notiflow must be active and published. Flags: - `--recipient` / `-r` `` — a recipient's external id (repeatable). - `--topic` `` — run to all subscribers of a topic instead of explicit recipients. - `--data` / `-d` `` — a JSON object of template/condition variables. - `--actor` `` — the external id of the actor who caused the event. - `--draft` — run the current **unpublished draft** instead of the published version. The notiflow must be active, but need not be published. You must provide at least one `--recipient` or a `--topic`. ```bash # Run the published version notiflows notiflow run welcome-series \ -r user_123 \ --data '{"first_name":"Ada"}' # Multiple recipients + an actor notiflows notiflow run order-shipped \ -r user_123 -r user_456 \ --actor user_789 \ --data '{"order_id":"ORD-1"}' # Target a topic notiflows notiflow run product-updates --topic release-notes # Test-run the current unpublished draft notiflows notiflow run welcome-series --draft -r user_123 ``` ## Channels Channels are configured in the dashboard; the CLI surface is read-only. ### `channel list` List the project's configured delivery channels. ```bash notiflows channel list ``` ### `channel get ` Show one channel by handle. ```bash notiflows channel get transactional-email ``` ## Related - [Notiflows as code](/docs/cli/notiflows-as-code) — the file format these commands read and write. - [CI/CD](/docs/cli/ci-cd) — running these commands in a pipeline. - [MCP server](/docs/ai/mcp) — the same operations as MCP tools inside an AI editor. --- # Notiflows as code > The on-disk format the Notiflows CLI uses — notiflows.json project config, per-notiflow notiflow.json with a flat steps array, extracted template bodies, and environment-variable substitution. Source: https://notiflows.com/docs/cli/notiflows-as-code --- title: Notiflows as code description: The on-disk format the Notiflows CLI uses — notiflows.json project config, per-notiflow notiflow.json with a flat steps array, extracted template bodies, and environment-variable substitution. --- The CLI maps your project's notiflows to a directory tree you can commit to git, review in pull requests, and deploy through CI. The format is a near 1:1 mapping of the [Management API](/docs/api) request body, with a couple of ergonomic conventions (body extraction, env-var substitution) on top. ## Project config — `notiflows.json` `notiflows init` creates a `notiflows.json` at the root of your repo: ```json { "notiflowsDir": ".notiflows", "project": "my-app" } ``` - `notiflowsDir` — where the notiflow working tree lives (defaults to `.notiflows`). Notiflows are stored under `/notiflows//`. - `project` — the project slug to operate on. Optional here; it can also come from `--project` or `NOTIFLOWS_PROJECT` (see [resolution precedence](/docs/cli#authenticate)). The CLI discovers `notiflows.json` by walking up from the current directory, so you can run commands from anywhere inside the repo. ## Directory layout A realistic working tree: ``` my-app/ notiflows.json .notiflows/ notiflows/ welcome-series/ notiflow.json # name, active flag, flat steps array, __meta steps/ email_1/ body.html # email template body, extracted for clean diffs email_2/ body.html order-shipped/ notiflow.json steps/ sms_1/ body.txt in_app_1/ body.md ``` The directory name **is** the notiflow handle and is immutable — renaming the directory retargets a different notiflow. **Notiflow handles are hyphens-only** (`welcome-series`, not `welcome_series`). Step handles, by contrast, may use underscores (`email_1`). ## `notiflow.json` Each notiflow is a single `notiflow.json`: ```json { "$schema": "https://notiflows.com/schemas/notiflow.json", "name": "Welcome Series", "active": true, "include_in_preferences": true, "steps": [ { "handle": "trigger", "type": "trigger", "position": 0 }, { "handle": "email_1", "type": "channel", "position": 1, "channel_type": "email", "channel_handle": "transactional-email", "template": { "channel_type": "email", "data": { "subject": "Welcome, {{ data.first_name }}!", "content_type": "html", "body@": "steps/email_1/body.html" } } }, { "handle": "wait_1", "type": "wait", "position": 2, "settings": { "duration": 1, "duration_unit": "days" } }, { "handle": "email_2", "type": "channel", "position": 3, "channel_type": "email", "channel_handle": "transactional-email", "template": { "channel_type": "email", "data": { "subject": "Getting started", "content_type": "html", "body@": "steps/email_2/body.html" } } }, { "handle": "end", "type": "end", "position": 4 } ], "__meta": { "version": 3, "published_version": 2, "status": "draft", "has_unpublished_changes": true, "sha": "a1b2c3...", "created_by": { "type": "account_token", "id": "...", "name": "ci-deploy" }, "published_by": { "type": "member", "id": "...", "name": "Ada Lovelace" } } } ``` ### Steps are a flat array — no edges `steps` is a **flat array** that mirrors the backend's steps-only schema. There are no edges; ordering and branching are encoded on the steps themselves: - `handle` — the stable step id (`trigger`, `email_1`, `end`). - `type` — one of `trigger`, `end`, `channel`, `condition`, `wait`, `digest`, `throttle`. - `position` — order within the step's scope (the root sequence, or within a branch). - `channel_type` / `channel_handle` — for a `channel` step, which channel to deliver through (`email`, `sms`, `in_app`, `mobile_push`, `web_push`, `chat`, `webhook`). - `parent_step_handle` / `branch_handle` — for a child of a **condition** step: which condition it belongs to (`parent_step_handle`) and which branch of that condition (`branch_handle`). - `settings` — type-specific settings. **Wait, digest, and throttle** use `duration` + `duration_unit` (`seconds` | `minutes` | `hours` | `days`); throttle adds `throttle_key`, digest adds `digest_key`. - `conditions` — optional gate conditions (groups of `{ property, operator, value }`) that decide whether a step runs. **Never strip the `trigger` and `end` steps.** Every notiflow keeps a terminal `trigger` step (position 0) and an `end` step. An upsert sends the **full** flat steps array — any step you omit is removed on the server. The CLI round-trips whatever the server returns; preserve the terminal steps when editing by hand. ### Template body extraction (`@`-suffix keys) To keep diffs readable, a channel step's main body field is extracted out of `template.data` into its own file under `steps//`, and replaced inline by a pointer key with an `@` suffix: ```json "data": { "subject": "Welcome!", "content_type": "html", "body@": "steps/email_1/body.html" } ``` `body@` points at the file; on push, the CLI reads the file back and inlines it as the real `body` key. The extension is chosen by channel type: | Channel type | Body file | |---|---| | `email` | `body.html` | | `in_app`, `chat` | `body.md` | | `sms`, `mobile_push`, `web_push` | `body.txt` | | `webhook` | `body.json` | Channel-step bodies **must be non-empty** — every channel template requires `body` (and `content_type`) server-side. ### Environment-variable substitution Values in your files can reference environment variables with `${ENV_VAR}` syntax; the CLI substitutes them at push time. This keeps provider ids, endpoints, and other per-environment values out of git while staying declarative. ### The read-only `__meta` block `__meta` is a **read-only** mirror of the notiflow's current state — `version`, `published_version`, `status`, `has_unpublished_changes`, the content `sha`, and who created the version (`created_by` / `published_by`). It (and `$schema`) are stripped before any push, so read-only data is never echoed back to the server. The `sha` powers conflict-safe pushes — see [pushing safely](/docs/cli/commands#sync). ## Related - [Command reference](/docs/cli/commands) — the commands that operate on this format. - [CI/CD](/docs/cli/ci-cd) — deploying notiflows from your pipeline. - [Building notiflows](/docs/learn/building-notiflows/overview) — the step types explained conceptually. --- # Broadcasts > Send notifications to everyone subscribed to a topic Source: https://notiflows.com/docs/concepts/broadcasts --- title: Broadcasts description: Send notifications to everyone subscribed to a topic --- ## Broadcasts Broadcasts let you send notifications to all users who care about a specific topic. Users subscribe to topics they want to follow, and when you broadcast to that topic, everyone subscribed gets notified automatically. ### What are Broadcasts? A broadcast is a notification sent to a topic. Instead of specifying individual recipients, you send to a topic, and Notiflows delivers the message to everyone subscribed to that topic. It's pub-sub for notifications. Think of broadcasts as sending an announcement to a channel or group. You don't manage the member list—users subscribe themselves, and you just broadcast to the topic. ### Topics Topics are lightweight identifiers that represent things users might want to follow in your app. A topic is just a string—no complex setup, no hierarchies. **Real-world topic examples:** - `thread-feature-requests` - A discussion thread about feature requests - `task-homepage-redesign` - Updates on a specific task - `pr-auth-refactor` - Comments and changes on a pull request - `project-mobile-app` - Updates about the mobile app project - `team-engineering` - Engineering team announcements - `issue-login-bug` - Activity on a specific issue Topics should map naturally to resources and concepts in your application. If users can "follow" something in your app, that's a good candidate for a topic. ### How Broadcasts Work **1. Users Subscribe** When users want to follow something, they subscribe to the topic. This could happen when they: - Join a discussion thread - Get assigned to a task - Watch a pull request - Opt into team updates - Follow a project **2. You Broadcast** When something happens that subscribers should know about, trigger a notiflow for that topic: ```typescript await notiflows.trigger({ notiflow: 'comment-added', topic: 'thread-feature-requests', data: { comment: 'I agree, we should prioritize this', author: 'Sarah' } }) ``` **3. Automatic Delivery** Notiflows finds all users subscribed to `thread-feature-requests` and sends them the notification. Could be 3 users, could be 3,000—same API call. **4. Users Unsubscribe** Users can unsubscribe anytime. Once unsubscribed, they won't receive future broadcasts to that topic. They can resubscribe later if they change their mind. ### Broadcast Use Cases **Discussion Threads** Let users subscribe to threads they participate in or want to follow. When someone comments, broadcast to the thread topic so all subscribers get notified. **Task and Issue Tracking** When users are assigned to tasks or watching issues, subscribe them to those topics. Broadcast updates, status changes, and comments to keep everyone in the loop. **Project Updates** Create topics for projects or teams. Users subscribe to projects they're involved in, and you broadcast important updates to those topics. **Document Collaboration** Subscribe users to documents they're editing or reviewing. Broadcast when someone comments, makes changes, or mentions them. **Pull Request Activity** Let users watch PRs they care about. Broadcast when someone reviews, comments, or updates the PR so watchers stay informed without checking GitHub constantly. ### Subscription Management **Automatic Subscriptions** Subscribe users automatically when their actions imply interest: - They create something (they probably want to follow it) - They're assigned to something (they need to stay updated) - They comment (they're engaged with the discussion) **Manual Subscriptions** Give users subscribe/unsubscribe buttons throughout your interface so they can control what they follow. **Bulk Operations** Subscribe multiple users at once when appropriate: - All team members when a new project starts - Everyone mentioned in a document - All assignees on a task **Listing Subscriptions** Let users see all topics they're subscribed to and manage their subscriptions from a single interface. ### Best Practices **Descriptive Topic Names** Use topic identifiers that clearly indicate what users are subscribing to. Include context in the name: - Good: `thread-feature-requests`, `task-auth-bug-fix` - Less clear: `thread-1234`, `task-567` **Map to Your Domain** Topics should feel natural for your application. If you have threads, PRs, tasks, or projects, those become topics. Don't force a different model. **Subscribe at the Right Time** Think about when subscription makes sense: - When users explicitly click "subscribe" or "watch" - When they take an action that implies interest (commenting, creating) - When they're assigned or mentioned **Respect Unsubscribes** Always honor unsubscribe requests immediately. Never re-subscribe someone without their explicit action. **Combine with Preferences** Broadcasts work alongside user preferences. A user might be subscribed to a topic but have preferences that control how they receive those notifications (email only, push only, etc.). **Clean Up Old Topics** When resources are deleted or archived (closed threads, completed tasks), consider whether you need to maintain the topic. Unsubscribing all users or removing inactive topics keeps things tidy. ### Topics vs. Direct Notifications Use broadcasts and topics when: - Multiple users should receive the same notification - Users want control over what they follow - The recipient list changes over time - You're broadcasting updates about shared resources Use direct notifications (specifying user IDs) when: - Notifying someone about their personal account - Sending transactional messages (receipts, confirmations) - The notification is specific to one user - The recipient is known and fixed ### Integration with Notiflows Broadcasts integrate seamlessly with the rest of Notiflows: **Preferences Still Apply** Even if a user is subscribed to a topic, their preference settings control delivery. If they've disabled email, they won't get email notifications for broadcasts—but they might still get in-app notifications. **Templates Work the Same** Use the same notiflow templates for broadcasts as you do for direct notifications. The only difference is how you specify recipients. **Channels Work Automatically** Broadcasts go through all configured channels (in-app, email, push, etc.) just like direct notifications, respecting channel availability and user preferences. **One Notiflow, Many Recipients** When you trigger a broadcast, Notiflows processes the notiflow once and then efficiently delivers to all subscribers. You get all the benefits of templates, channels, and preferences without managing recipient lists. By using broadcasts, you give users control over what they follow while simplifying how you send notifications. No more maintaining recipient lists or querying who should receive a notification—users manage their own subscriptions, and you just broadcast. --- # Channel Settings > Storing recipient-specific data for channels like device tokens and chat connections Source: https://notiflows.com/docs/concepts/channel-settings --- title: Channel Settings description: Storing recipient-specific data for channels like device tokens and chat connections --- ## Channel Settings Channel settings store recipient-specific data required by certain channels to deliver notifications. While channels define the provider configuration at the project level, channel settings hold the per-user data that connects a user to that channel — such as device tokens for mobile push or Slack connection details for chat. ### Why Channel Settings? Not all channels need user-specific data. Email and SMS channels use contact information already on the user record (email address, phone number). But some channels require additional, provider-specific data to deliver messages: - **Mobile push channels** need device tokens registered by the user's device - **Chat channels** need identifiers that link the user to a workspace or conversation Channel settings bridge this gap. They are set once (typically during device registration or OAuth connection) and used automatically whenever a notiflow delivers a notification through that channel. Web push subscriptions are managed through a separate [Channel Subscriptions](/docs/concepts/channel-subscriptions) API, not channel settings. ### How Channel Settings Work Channel settings are stored as a mapping between a user and a channel. Each entry includes provider-specific data that Notiflows uses at delivery time. When a notiflow reaches a channel step, Notiflows: 1. Looks up the recipient's channel settings for the target channel 2. Extracts the provider-specific data (tokens, connection IDs) 3. Uses that data to deliver the notification through the provider If no channel settings exist for a user on a given channel, delivery through that channel is skipped for that user. ### Provider Data Requirements Each provider type requires specific data in channel settings. #### Mobile Push (APNs, FCM) Mobile push channels store an array of device tokens. A user can have multiple tokens if they use your app on several devices. ```json { "settings": { "device_tokens": ["token_for_iphone", "token_for_ipad"] } } ``` | Field | Type | Description | | --- | --- | --- | | `device_tokens` | `string[]` | Array of device tokens registered by the user's devices | When delivering a push notification, Notiflows sends to all registered device tokens. This ensures the user receives the notification regardless of which device they're currently using. **When to set push tokens:** - After the user grants push permission in your app - On each app launch (tokens can rotate) - When migrating from another push provider **When to remove tokens:** - When the user logs out of your app on a device - When a token is rejected by the push provider (expired or invalid) #### Chat Channels (Slack) Slack channel settings identify where to deliver messages for a user within a Slack workspace. ```json { "settings": { "slack_channel_id": "C01ABCDEF", "slack_user_id": "U01ABCDEF" } } ``` | Field | Type | Description | | --- | --- | --- | | `slack_channel_id` | `string` | The Slack channel to post notifications to | | `slack_user_id` | `string` | The Slack user to send direct messages to | At least one of these fields must be provided. If both are set, Notiflows uses the `slack_user_id` for direct messages. **When to set Slack data:** - After the user completes Slack OAuth and connects their account - When an admin maps a user to a Slack identity ### Managing Channel Settings Channel settings are managed through the API, typically from your backend when a user connects a device or links an account. Channel settings use upsert semantics — if settings already exist for the user-channel pair, they are replaced. You can set, retrieve, and remove channel settings through both the Admin API (server-to-server) and the User API (client-side). Channel settings are also visible in the Notiflows dashboard under each user's **Channel Settings** tab. Set or update channel settings for a user from your backend. Retrieve a user's current channel settings. Remove channel settings when a user disconnects a device or unlinks an account. Set channel settings from the client side. ### Channel Settings vs. Preferences Channel settings and preferences serve different purposes: | | Channel Settings | Preferences | | --- | --- | --- | | **Purpose** | Store data needed to deliver through a channel | Control whether a user wants to receive notifications | | **Set by** | Your application (programmatically) | The user (through a preference center) | | **Example** | Device tokens, Slack user IDs | "Disable all SMS notifications" | | **Effect when missing** | Delivery is skipped (can't reach user) | Delivery proceeds (user hasn't opted out) | Both work together at delivery time. Notiflows first checks preferences to see if the user wants the notification, then checks channel settings to see if delivery is possible. ### Best Practices - **Set tokens early**: Register device tokens as soon as push permission is granted, and refresh them on each app launch - **Clean up stale tokens**: Remove tokens that are no longer valid to avoid unnecessary delivery attempts and provider errors - **Handle multiple devices**: Users may have several devices — store all active tokens so notifications reach every device - **Secure the flow**: Always set channel settings from your backend using the Admin API, never from client-side code, as it requires your secret key - **Verify before storing**: Validate tokens and connection IDs before saving them as channel settings to catch errors early --- # Channel Subscriptions > Managing per-device push subscriptions for web push and other subscription-based channels Source: https://notiflows.com/docs/concepts/channel-subscriptions --- title: Channel Subscriptions description: Managing per-device push subscriptions for web push and other subscription-based channels --- ## Channel Subscriptions Channel subscriptions store individual push subscriptions for a user on a given channel. Unlike [channel settings](/docs/concepts/channel-settings), which hold a single blob of configuration per user-channel pair, channel subscriptions are designed for cases where a user has **multiple independent subscriptions** — each with its own lifecycle. The primary use case today is **web push**, where each browser or device creates its own push subscription that can be added or removed independently. ### Why Separate from Channel Settings? Push subscriptions have fundamentally different characteristics than channel settings: | | Channel Settings | Channel Subscriptions | | --- | --- | --- | | **Cardinality** | One per user-channel pair | Many per user-channel pair | | **Lifecycle** | Set once, replaced on update | Each subscription added/removed independently | | **Semantics** | Upsert (replace entire blob) | Individual add/remove operations | | **Use case** | Device tokens, chat connections | Browser push subscriptions | A user might have web push subscriptions from Chrome on their laptop, Firefox on their desktop, and Safari on their phone. Each subscription has its own endpoint and encryption keys, and removing one shouldn't affect the others. ### How Channel Subscriptions Work Each channel subscription is stored as a separate record with: | Field | Description | | --- | --- | | `identifier` | A unique key for deduplication (for web push, the push service endpoint URL) | | `settings` | Provider-specific data as JSON (for web push, `endpoint`, `p256dh`, and `auth`) | When a notiflow reaches a web push channel step, Notiflows: 1. Loads all channel subscriptions for the recipient on that channel 2. Sends the notification to every registered subscription 3. Each browser/device receives the push independently If a user has no subscriptions for a channel, delivery through that channel is skipped. ### Managing Subscriptions #### Adding a Subscription Register a subscription by sending a `POST` request with an `identifier` and provider-specific `settings`: ```json POST /user/v1/channels//subscriptions { "identifier": "https://fcm.googleapis.com/fcm/send/...", "settings": { "endpoint": "https://fcm.googleapis.com/fcm/send/...", "p256dh": "BOxxx...", "auth": "xxx..." } } ``` | Field | Type | Description | | --- | --- | --- | | `identifier` | `string` | Unique key for this subscription. For web push, the push endpoint URL. | | `settings` | `object` | Provider-specific data, validated per channel type. | | `settings.endpoint` | `string` | The push service endpoint URL from `PushSubscription`. | | `settings.p256dh` | `string` | The P-256 Diffie-Hellman public key from `PushSubscription.keys`. | | `settings.auth` | `string` | The authentication secret from `PushSubscription.keys`. | **Deduplication**: If a subscription with the same `identifier` already exists, the `settings` are updated in place. No duplicate is created. This makes it safe to re-register on every page load. **Limit**: A maximum of 25 subscriptions per user-channel pair is enforced. #### Removing a Subscription Remove a specific subscription by sending a `DELETE` request with its `identifier`: ```json DELETE /user/v1/channels//subscriptions { "identifier": "https://fcm.googleapis.com/fcm/send/..." } ``` Only the matching subscription is removed — other subscriptions for the same user and channel are unaffected. ### Full Integration Example See the [Web Push setup guide](/docs/channels-providers/web-push) for a complete example of registering a service worker, subscribing to push, and sending the subscription to Notiflows. ```javascript // After getting browser push permission const subscription = await registration.pushManager.subscribe({ userVisibleOnly: true, applicationServerKey: '', }); const { endpoint, keys } = subscription.toJSON(); // Register with Notiflows await fetch('https://api.notiflows.com/user/v1/channels//subscriptions', { method: 'POST', headers: { 'Content-Type': 'application/json', 'x-notiflows-api-key': '', 'x-notiflows-user-key': '', }, body: JSON.stringify({ identifier: endpoint, settings: { endpoint, p256dh: keys.p256dh, auth: keys.auth, }, }), }); ``` ### When to Add Subscriptions - After the user grants notification permission and `PushManager.subscribe()` succeeds - On each page load — re-check the existing subscription and re-register it (the upsert ensures no duplicates) - After a subscription changes (browsers can rotate push subscriptions) ### When to Remove Subscriptions - When the user explicitly opts out of push notifications in your app - When a push delivery fails with a `410 Gone` status (the subscription has expired) - When the user logs out and you want to stop sending them notifications on that browser ### API Reference List all subscriptions for a channel. Register a push subscription from the client side. Remove a push subscription when a user unsubscribes. --- # Channels > Learn about notification channels Source: https://notiflows.com/docs/concepts/channels --- title: Channels description: Learn about notification channels --- ## Channels Channels are the communication methods through which notifications are delivered to users. Each channel represents a configured connection to a delivery provider that enables sending messages through a specific medium. ### What is a Channel? A channel is a configured instance of a communication method in Notiflows. It connects your notiflows to external providers (like email services, SMS gateways, or push notification services) that actually deliver messages to users. Channels serve as the bridge between your notiflows and the various ways users can receive notifications. When a notiflow needs to deliver a notification, it uses the appropriate channel to send the message through the configured provider. ### Channel Types Notiflows supports several types of channels: **Email Channels** Email channels connect to email service providers to send email notifications. You can configure multiple email channels for different purposes (transactional emails, marketing emails, system alerts). Supported providers include services like SendGrid, Mailgun, Amazon SES, Resend, and others. **SMS Channels** SMS channels enable sending text messages to users' phone numbers. These connect to SMS providers such as Twilio. SMS channels require phone numbers in E.164 format. **Push Notification Channels** Push channels deliver notifications to mobile devices and web browsers. They connect to services like Apple Push Notification Service (APNs), Firebase Cloud Messaging (FCM), or web push services. Push channels require device tokens for each recipient. **In-App Channels** In-app channels deliver notifications directly within your application interface. These don't require external providers and are managed entirely by Notiflows. In-app notifications can appear as feeds, toasts, banners, or other UI elements. **Chat Channels** Chat channels send notifications to messaging platforms like Slack, Microsoft Teams, Discord, or WhatsApp. These connect to the respective platform APIs to deliver messages to channels or direct messages. **Webhook Channels** Webhook channels allow you to send notifications to custom endpoints or enable your customers to set up their own webhook integrations. This provides flexibility for custom delivery methods or integrations with other systems. ### Channel Configuration Each channel requires specific configuration settings: **Provider Credentials** Channels need authentication credentials to connect to their providers. This might include API keys, access tokens, service account credentials, or other authentication methods required by the provider. **Channel Settings** Depending on the channel type, you may need to configure: - Sender information (email addresses, phone numbers, app identifiers) - Provider-specific settings (tracking options, authentication methods) **Environment-Specific Configuration** Channels are configured per environment (development, staging, production). This allows you to use test credentials in development and production credentials in production, ensuring safe testing without affecting real users. ### Managing Channels **Creating Channels** Channels are created through the Notiflows dashboard or API. When you create a channel, you provide the necessary configuration and credentials. Once created, a channel is available across all environments in your account, though each environment may have different configuration values. **Channel Availability** After creation, channels are immediately available for use in your notiflows. You can reference channels by their unique identifier when configuring channel steps in your notiflows. **Updating Channels** You can update channel configuration at any time. Changes take effect immediately and don't require versioning. This allows you to update credentials, modify settings, or switch providers without recreating the channel. **Channel Status** Channels can be active or inactive. Active channels are available for use in notiflows. Inactive channels are disabled and won't be used for delivery, which is useful for temporarily disabling a channel without deleting it. ### Using Channels in Notiflows **Channel Steps** In your notiflows, you add channel steps that specify which channel to use for delivery. Each channel step: - References a specific channel - Uses a template to format the notification content - Can include conditional logic to determine when to use the channel **Multiple Channels** A single notiflow can use multiple channels, allowing you to send the same notification through email, SMS, and push simultaneously, or conditionally choose channels based on user preferences or other factors. **Channel Data** Some channels require recipient-specific data. For example: - Push channels need device tokens - SMS channels need phone numbers - Email channels need email addresses This information is stored as channel data on user records and is automatically used when delivering through that channel. ### Best Practices **Provider Selection** Choose providers based on: - Reliability and uptime - Delivery rates and reputation - Cost and pricing structure - Regional coverage and compliance - Features and capabilities you need **Multiple Providers** Consider configuring multiple channels of the same type with different providers. This provides redundancy and allows you to switch providers if needed without downtime. **Environment Separation** Always use separate channel configurations for development and production. This prevents test messages from being sent to real users and protects your production provider accounts. **Credential Security** Store channel credentials securely and rotate them regularly. Never commit credentials to version control or expose them in client-side code. **Monitoring** Monitor channel performance, delivery rates, and error rates. This helps you identify issues early and choose the best channels for different use cases. **Testing** Test channels thoroughly in development before using them in production. Send test messages to verify configuration and delivery. By properly configuring and managing channels, you ensure reliable delivery of notifications through the communication methods your users prefer, while maintaining flexibility to adapt as your needs change. --- # Deliveries > Learn about notification deliveries Source: https://notiflows.com/docs/concepts/deliveries --- title: Deliveries description: Learn about notification deliveries --- ## Deliveries A delivery represents a single attempt to send a notification through a specific channel to a user. While a notification contains the information to be communicated, a delivery tracks the actual transmission of that information through a particular communication method. ### Understanding Deliveries When a notification is created by a notiflow, it can be delivered through multiple channels. Each channel delivery is tracked as a separate delivery record. This allows you to monitor the success, failure, or status of each delivery attempt independently. For example, if a notification is sent via both email and SMS: - There is one notification (the information being communicated) - There are two deliveries (one for email, one for SMS) ### Delivery Lifecycle A delivery goes through several states during its lifecycle: **Pending** When a delivery is first created, it starts in a pending state. This means the delivery has been queued but hasn't yet been sent to the channel provider. **Processing** The delivery moves to processing when Notiflows begins the actual transmission process, communicating with the channel provider (email service, SMS gateway, push notification service, etc.). **Sent** A delivery is marked as sent when it has been successfully transmitted to the channel provider. This doesn't guarantee the user has received it, but indicates the provider accepted the delivery request. **Delivered** For channels that support delivery confirmation (like email read receipts or push notification delivery confirmations), the delivery status updates to delivered when the provider confirms the message reached the user's device or inbox. **Failed** If a delivery cannot be completed, it's marked as failed. This can happen for various reasons: - Invalid recipient information (bad email address, phone number) - Channel provider errors or outages - Rate limiting or quota exceeded - Network issues or timeouts **Bounced** Some channels support bounce detection. If a delivery bounces (email is rejected, phone number is invalid), it's marked accordingly so you can update your user data. ### Delivery Tracking Each delivery includes detailed information: - **Channel**: Which communication method was used (email, SMS, push, etc.) - **Status**: Current state of the delivery - **Timestamps**: When the delivery was created, sent, and (if applicable) delivered - **Provider Response**: Any error messages or status codes from the channel provider - **Retry Information**: Whether the delivery was retried and how many attempts were made ### Retry Logic Notiflows automatically retries failed deliveries using an exponential backoff strategy. This means: - Initial retries happen quickly - Subsequent retries wait longer between attempts - There's a maximum number of retry attempts - Some failure types (like invalid recipient information) are not retried This ensures temporary issues (network problems, provider outages) don't permanently prevent delivery while avoiding unnecessary retries for permanent failures. ### Delivery Methods Different channels have different delivery characteristics: **Email Deliveries** - Can be tracked through open rates and click tracking (if enabled) - May bounce if the email address is invalid - Can be marked as spam by email providers - Delivery confirmation depends on the email provider **SMS Deliveries** - Typically have faster delivery confirmation - May fail if the phone number is invalid or the carrier blocks the message - Have character limits that affect message formatting **Push Notification Deliveries** - Require valid device tokens - May fail if the app is uninstalled or tokens are invalid - Delivery is usually confirmed quickly by the push service **In-App Deliveries** - Delivered immediately when the user is online - Stored for later delivery if the user is offline - Don't require external providers ### Monitoring Deliveries You can monitor deliveries through: - **Delivery Logs**: View all deliveries, filter by status, channel, or time period - **Analytics**: Track delivery rates, success rates, and failure reasons - **Webhooks**: Receive real-time notifications about delivery status changes - **API Queries**: Programmatically retrieve delivery information ### Best Practices - **Monitor Failure Rates**: Regularly check delivery logs to identify patterns in failures - **Update User Data**: When deliveries fail due to invalid contact information, update your user records - **Respect Rate Limits**: Be aware of channel provider rate limits to avoid delivery failures - **Handle Bounces**: Implement logic to handle bounced deliveries and update user preferences accordingly - **Track Metrics**: Use delivery data to understand which channels work best for your users ### Delivery vs. Notification Remember the key distinction: - **Notification**: The information being communicated (created once per event) - **Delivery**: An attempt to send that notification through a specific channel (one per channel per notification) This separation allows you to: - Track which channels successfully delivered the information - Retry failed deliveries without recreating the notification - Provide users with delivery status information - Analyze channel performance and reliability By understanding deliveries, you gain visibility into how your notifications are reaching users and can optimize your notification strategy based on real delivery data. --- # Notifications > Understanding notifications Source: https://notiflows.com/docs/concepts/notifications --- title: Notifications description: Understanding notifications --- ## Notifications A notification in Notiflows represents a single notification event created by a notiflow. It contains the information and content that will be communicated to users, and it can be delivered through one or more channels. ### What is a Notification? When you trigger a notiflow with user input, the notiflow processes that input and generates a notification. This notification is the core information unit that contains: - The message content - The target user - Any relevant data or context - Metadata about when and why it was created Think of a notification as the "what" - the actual information being communicated - while deliveries (covered in the next section) represent the "how" - the specific channel through which that information reaches the user. ### Notification Lifecycle **Creation** A notification is created when a notiflow processes a trigger request. The notiflow takes the input data, applies any configured logic, templates, or transformations, and produces a notification object. **Processing** Once created, the notification contains all the information needed for delivery. The system evaluates user preferences, channel availability, and other factors to determine how the notification should be delivered. **Delivery** The notification can be delivered through multiple channels simultaneously or sequentially, depending on your notiflow configuration. Each delivery to a specific channel is tracked separately (see the Deliveries concept for more details). ### Key Characteristics **Single Source of Truth** A notification represents one logical event or piece of information. Even if it's delivered through multiple channels (email, SMS, push notification), there's still just one notification containing that information. **Channel-Agnostic Content** The notification contains the core message and data, which can then be formatted appropriately for each delivery channel. For example, the same notification might be delivered as a short SMS, a detailed email, and a push notification, all containing the same essential information but formatted for each medium. **User-Specific** Each notification is associated with a specific user. When a notiflow processes input, it creates notifications for the target users specified in the trigger. **Immutable** Once created, a notification's core content doesn't change. If you need to send updated information, you create a new notification rather than modifying an existing one. ### Notification Data A notification typically includes: - **Content**: The message text, subject, or other content elements - **Metadata**: Information about when it was created, which notiflow generated it, and any relevant context - **User Data**: References to the target user and any user-specific information used in personalization - **Custom Data**: Any additional information specific to your use case ### Use Cases Notifications are ideal for: - **Event-Driven Communication**: Alerting users about actions, updates, or changes - **Transactional Messages**: Confirming purchases, password resets, or account changes - **Informational Updates**: Sharing news, updates, or important information - **Engagement**: Welcoming new users, re-engaging inactive users, or sharing personalized content ### Notifications vs. Deliveries It's important to understand the distinction: - **Notification**: The information being communicated (one per event) - **Delivery**: An instance of that notification being sent through a specific channel (multiple per notification) For example, if you create a notification about a new order and deliver it via both email and SMS, you have: - One notification (the order information) - Two deliveries (one email delivery, one SMS delivery) This separation allows you to track the notification as a whole while also monitoring the success of each delivery channel independently. ### Best Practices - **Clear Purpose**: Each notification should have a clear, single purpose - **Relevant Content**: Include all necessary information without overwhelming the user - **Timely Creation**: Create notifications when events occur, not when you want to send them - **Proper Categorization**: Use metadata to categorize notifications for analytics and user preferences By understanding notifications as the core information units in Notiflows, you can design notiflows that create clear, purposeful communications that users value and engage with. --- # Notiflows > Understanding Notiflows Source: https://notiflows.com/docs/concepts/notiflows --- title: Notiflows description: Understanding Notiflows --- ## Notiflows Notiflows are the fundamental orchestration units that define how your application communicates with users. Think of a notiflow as a blueprint that outlines the complete journey of a notification from creation to delivery across one or more communication channels. ### What is a Notiflow? A notiflow is a structured sequence of steps that processes user input and generates notifications. Each notiflow is designed to handle a specific type of communication scenario, such as welcoming new users, alerting about important updates, or sending transactional confirmations. When you trigger a notiflow, it processes the provided data, applies any configured logic, and creates notifications that can be delivered through multiple channels simultaneously or sequentially. ### Core Components Every notiflow consists of several key elements: **Entry Point** The notiflow begins when your application sends a trigger request. This request includes information about the event, the target user, and any relevant data needed to personalize the notification. **Processing Steps** These steps define the logic and flow of your notification: - **Channel Steps**: Send notifications through specific delivery methods (email, SMS, push, etc.) - **Wait Steps**: Delay execution for a specified duration before continuing - **Digest Steps**: Group multiple trigger events into a single notification - **Throttle Steps**: Limit how frequently notifications are sent to prevent overwhelming recipients **Output** The notiflow produces one or more notifications, each of which can be delivered through configured channels. ### Designing Effective Notiflows **Single Purpose Principle** Each notiflow should handle one specific type of communication. This approach makes your notification system easier to maintain, test, and modify. For example, create separate notiflows for "password reset" and "welcome email" rather than combining them. **User-Centric Design** Consider how users want to receive information. Group related notifications together when appropriate, and respect user preferences for channel selection and frequency. **Modularity** Break complex notification scenarios into smaller, reusable notiflows. You can chain notiflows together or trigger them independently based on different conditions. ### Notiflow Lifecycle A notiflow can exist in different states: - **Active**: Ready to process triggers and generate notifications - **Inactive**: Disabled and cannot be triggered ### Best Practices - **Naming Conventions**: Use clear, descriptive names that indicate the notiflow's purpose (e.g., `order-confirmation`, `weekly-digest`) - **Version Control**: Track changes to your notiflows to maintain a history of modifications - **Testing**: Test notiflows thoroughly in development environments before deploying to production - **Documentation**: Document the expected input data and behavior of each notiflow for your team By thoughtfully designing your notiflows, you create a robust, scalable notification system that delivers the right message to the right user at the right time through their preferred channels. --- # Preferences > Learn about user preferences Source: https://notiflows.com/docs/concepts/preferences --- title: Preferences description: Learn about user preferences --- ## Preferences Preferences give users control over their notification experience. They allow individuals to customize which notifications they receive, through which channels, and when those notifications are delivered. ### Understanding Preferences A user's preference set is a collection of settings that determine their notification behavior. These settings can control: - Which notiflows they want to receive notifications from - Which channels they prefer for different types of notifications - When they want to receive notifications (quiet hours, time zones) - Overall notification frequency and volume ### Preference Levels Preferences in Notiflows operate at multiple levels, providing both granular control and sensible defaults. **Channel Type Preferences** Channel types are broad categories of communication methods, such as email, SMS, push notifications, or in-app messages. Users can set preferences at this level to control entire categories of notifications. For example, a user might disable all SMS notifications while keeping email enabled. **Channel Preferences** Channels are specific instances within channel types. For example, you might have multiple email channels for different purposes (transactional emails, marketing emails, system alerts). Channel preferences provide more granular control, allowing users to opt into or out of specific channels while keeping others active. Channel preferences take precedence over channel type preferences. If a user disables a specific channel, that setting overrides any channel type preference. **Notiflow Preferences** Users can control preferences for individual notiflows. This allows them to receive some types of notifications while opting out of others. For example, a user might want order confirmations but not promotional emails. ### Building a Preference Center A preference center is a user interface where users can view and modify their notification preferences. Here's how to implement one: **1. Establish Default Preferences** Create a default preference set that new users inherit when they sign up. This ensures all users start with a sensible baseline configuration that you can customize. **2. Retrieve Current Preferences** Use the preferences API to fetch a user's current preference settings. This data will inform what options to display and which toggles should be enabled or disabled. **3. Design the Interface** Create a user-friendly interface that: - Groups related preferences logically - Uses clear labels and descriptions - Provides immediate feedback when preferences are updated - Explains the impact of each preference choice **4. Update Preferences** When users make changes, use the preferences API to save their selections. Notiflows will immediately apply these preferences to future notifications. ### Preference Hierarchy When determining whether to send a notification, Notiflows evaluates preferences in this order: 1. **Notiflow-level settings**: If a notiflow is disabled for a user, no notifications are sent 2. **Channel preferences**: If a specific channel is disabled, that channel is skipped 3. **Channel type preferences**: If an entire channel type is disabled, all channels of that type are skipped 4. **Global preferences**: Overall user settings that apply broadly This hierarchy ensures that the most specific preference always takes precedence, giving users precise control while maintaining sensible defaults. ### Best Practices - **Respect User Choices**: Always honor user preferences and make it easy for them to change their mind - **Clear Communication**: Explain what each preference controls and how it affects their experience - **Sensible Defaults**: Set default preferences that work for most users, but allow easy customization - **Regular Review**: Periodically remind users to review their preferences, as their needs may change over time By implementing a robust preference system, you empower users to tailor their notification experience, leading to higher engagement, better user satisfaction, and reduced notification fatigue. --- # Subscriptions > Understanding subscriptions Source: https://notiflows.com/docs/concepts/subscriptions --- title: Subscriptions description: Understanding subscriptions --- ## Subscriptions Subscriptions enable users to opt into receiving notifications about specific topics. In Notiflows, users subscribe to topics, and when you trigger a notiflow for a topic, all users subscribed to that topic receive the notification. ### What are Subscriptions? A subscription is a relationship between a user and a topic. When a user subscribes to a topic, they indicate they want to receive notifications related to that topic. When you trigger a notiflow for a topic, Notiflows automatically delivers notifications to all users who have subscribed to that topic. This publish-subscribe pattern allows you to: - Send notifications to many users without managing individual recipient lists - Let users control which topics they want to follow - Efficiently broadcast messages to interested audiences - Scale notification delivery to large numbers of subscribers ### Topics In Notiflows, subscriptions are always to topics. A topic represents a category, subject, or area of interest that users can subscribe to. Topics are identified by unique topic identifiers that you define. Examples of topics might include: - `project-updates` - Updates about a specific project - `team-announcements` - Announcements for a team - `product-releases` - New product or feature releases - `security-alerts` - Important security notifications - `weekly-digest` - Weekly summary notifications ### How Subscriptions Work **1. Users Subscribe to Topics** Users can subscribe to topics through your application interface or API. When a user subscribes to a topic, Notiflows records that relationship. **2. Trigger Notiflows for Topics** When an event occurs that should notify subscribers, you trigger a notiflow for that topic. You don't need to specify individual recipients - Notiflows automatically identifies all subscribers. **3. Automatic Fan-Out** Notiflows automatically delivers the notification to all users subscribed to the topic. This happens efficiently, even for topics with thousands of subscribers. **4. Users Can Unsubscribe** Users can unsubscribe from topics at any time. Once unsubscribed, they won't receive future notifications for that topic, but they can resubscribe later if they choose. ### Managing Subscriptions **Subscribing Users** You can subscribe users to topics individually or in batches. When subscribing multiple users, you can subscribe up to 100 users at a time for efficiency. **Unsubscribing Users** Users can be unsubscribed from topics through your application or directly by the user. Unsubscribing immediately stops future notifications for that topic. **Listing Subscriptions** You can retrieve: - All topics a user is subscribed to - All users subscribed to a specific topic - Subscription details including when the subscription was created **Subscription Properties** You can attach custom properties to subscriptions. These properties store additional information about the subscription relationship, such as: - When the user subscribed - Subscription source (where they subscribed from) - User preferences specific to that topic - Any other metadata relevant to the subscription ### Use Cases **Broadcast Notifications** Send announcements, updates, or news to all users interested in a topic without managing recipient lists manually. **Topic-Based Alerts** Allow users to subscribe to alerts about specific entities (projects, teams, products) and notify all subscribers when relevant events occur. **Opt-In Communication** Give users control over which types of notifications they receive by letting them choose topics to subscribe to. **Scalable Messaging** Efficiently send notifications to large audiences. Whether a topic has 10 subscribers or 10,000, the process is the same. **User-Controlled Preferences** Combine subscriptions with user preferences to give users granular control over their notification experience. ### Subscription Best Practices **Clear Topic Naming** Use descriptive, consistent topic identifiers that clearly indicate what notifications users will receive. This helps users understand what they're subscribing to. **Topic Organization** Organize topics logically. Consider using hierarchical naming (e.g., `project:123:updates`) or categories to group related topics. **Subscription Management UI** Provide a clear interface where users can see their subscriptions, subscribe to new topics, and unsubscribe from topics they no longer want to follow. **Respect Unsubscribes** Always honor unsubscribe requests immediately. Don't send notifications to users who have unsubscribed from a topic. **Subscription Confirmation** Consider confirming subscriptions (especially for important topics) to ensure users intended to subscribe and to verify contact information. **Regular Cleanup** Periodically review subscriptions and remove inactive or invalid subscriptions to keep your subscriber lists current. ### Subscriptions vs. Direct Notifications Subscriptions are ideal when: - Multiple users need the same notification - Users should control what they receive - You want to broadcast to audiences - Recipient lists change frequently Direct notifications (specifying recipients in the trigger) are better when: - Notifying specific individuals about personal events - Sending transactional messages - The recipient list is small and known in advance - User choice isn't relevant ### Integration with Other Concepts **Subscriptions and Preferences** Subscriptions work alongside user preferences. A user might be subscribed to a topic but have preferences that control how they receive those notifications (which channels, frequency, etc.). **Subscriptions and Notiflows** When you trigger a notiflow for a topic, it automatically uses the subscription list to determine recipients. The notiflow processes once, but creates notifications for all subscribers. **Subscriptions and Users** Only registered users can subscribe to topics. Ensure users are properly identified in Notiflows before they can subscribe. By leveraging subscriptions, you create a flexible, scalable notification system that puts users in control while simplifying the process of broadcasting messages to interested audiences. --- # Users > Understanding users in Notiflows Source: https://notiflows.com/docs/concepts/users --- title: Users description: Understanding users in Notiflows --- ## Users Users are the individuals in your system who receive notifications. In Notiflows, a user represents a person who can be targeted by your notiflows and receive messages through various channels. ### User Identification Each user in Notiflows must have a unique identifier. This identifier serves as the primary way to reference the user across all operations. It's recommended to use your application's internal user ID (such as a database primary key) as the Notiflows user identifier. **Important Considerations:** - Choose identifiers that are stable and won't change over time - Avoid using email addresses or phone numbers as identifiers, as these can change - Use consistent identifier formats across your application ### User Attributes Users can have both standard and custom attributes that help personalize notifications and enable channel-specific delivery. **Standard Attributes:** - **id** (required): The unique identifier for the user - **email**: Primary email address, needed for email channel delivery - **name**: Full name or display name of the user - **avatar**: URL pointing to the user's profile picture - **phone_number**: Phone number in E.164 format, required for SMS delivery - **timezone**: User's time zone, useful for scheduling notifications at appropriate times **Custom Properties:** Beyond standard attributes, you can store any additional information as custom properties. These are key-value pairs that allow you to personalize notifications with user-specific data. For example, you might store subscription tier, language preference, or account status. When updating user properties, Notiflows performs a deep merge, meaning existing properties are updated with new values, and you can remove properties by setting them to `null`. ### Managing Users **Creating and Updating Users** Users are created or updated when you identify them in Notiflows. This can happen: - Explicitly through the user identification API - Automatically when a user is referenced in a notiflow trigger - During user registration or profile updates in your application **Retrieving User Information** You can fetch the current state of any user, including all their attributes and properties, using the user retrieval API. This is useful for displaying user information in dashboards or verifying user data. **Deleting Users** When a user is removed from your system, you can delete them from Notiflows. This action: - Prevents the user from receiving future notifications - Removes them from user lists and dashboards - Preserves historical notification data for audit purposes ### Special Cases **Guest Users** For scenarios where you need to notify someone who isn't yet a registered user (like sending an invitation email), you can create temporary user records. Use a unique identifier such as a prefixed ID (e.g., `guest_abc123`) or their email address. If they later register, you can merge the guest record with their permanent user account. **Multiple User Types** If your application has different types of users (customers, administrators, vendors, etc.), you can distinguish them by prefixing their identifiers. For example: `customer_123`, `admin_456`, `vendor_789`. This helps organize and filter users while maintaining clear separation. **Non-User Entities** For notifying entities that aren't people (like systems, services, or automated processes), consider using a different approach or modeling them as special user types with appropriate identifiers. ### Environment Isolation Users are scoped to specific environments (development, staging, production). Each environment maintains its own separate set of users. This ensures that test data doesn't interfere with production notifications and allows you to safely experiment in development environments. ### Best Practices - **Consistent Identification**: Always use the same identifier format across your application - **Regular Updates**: Keep user attributes current, especially contact information like email and phone numbers - **Privacy Considerations**: Only store necessary user data and respect privacy regulations - **Error Handling**: Implement fallbacks for cases where user data might be missing or incomplete By properly managing users in Notiflows, you ensure that notifications reach the right people with the right information, delivered through their preferred channels. --- # Quick Start > Send your first notification with Notiflows in minutes Source: https://notiflows.com/docs/getting-started/quick-start --- title: Quick Start description: Send your first notification with Notiflows in minutes --- This guide walks you through creating your first notiflow and triggering it from your application. By the end, you'll have a working notification flowing through Notiflows. ## Prerequisites - A Notiflows account ([sign up here](https://app.notiflows.com/signup)) - A project created in your Notiflows dashboard ## 1. Get Your API Keys Every project in Notiflows has its own set of API keys. You'll need both keys to trigger notiflows from your backend. 1. Open your project in the Notiflows dashboard 2. Navigate to **Settings** > **API Keys** 3. Copy your **API Key** (starts with `pk_`) and **Secret Key** (starts with `sk_`) Keep your secret key secure. Never expose it in client-side code or public repositories. Store your API keys as environment variables: ```bash export NOTIFLOWS_API_KEY=pk_your_api_key_here export NOTIFLOWS_SECRET=sk_your_secret_key_here ``` ## 2. Create Your First Notiflow A notiflow defines what happens when you trigger a notification. It's a sequence of steps that can include sending messages through different channels, adding delays, batching notifications, and more. 1. In your project, go to **Notiflows** 2. Click **Create notiflow** 3. Enter a name, for example: `welcome-email` 4. Click **Create** You'll be taken to the notiflow overview. Now let's build it. ## 3. Design the Notiflow Click on **Steps** to open the visual builder. Every notiflow starts with a **Trigger** node and ends with an **End** node. ### Add a Channel Step 1. From the right sidebar, drag an **Email** step onto the canvas 2. Connect it between the Trigger and End nodes 3. Click on the Email step to configure it ### Configure the Channel 1. Select an email channel (you'll need to have one configured in **Channels**) 2. Set up your email template: - **Subject**: `Welcome to our platform, {{ data.name }}!` - **Body**: Design your email content using the template editor You can use Liquid variables like `{{ data.name }}` or `{{ recipient.email }}` that will be populated from the data you send when triggering the notiflow. Don't have a channel configured yet? Head to **Channels** in your project and connect an email provider like Resend, SendGrid, or Amazon SES. ## 4. Publish Your Notiflow Notiflows use versioning to help you safely iterate on your notification logic. When you make changes, they're saved as a draft version. To make your notiflow live, you need to publish it. 1. Go to the **Changes** tab 2. You'll see your draft version listed 3. Click **Publish** 4. Confirm the publish action Your notiflow is now live and ready to receive triggers. ## 5. Activate the Notiflow Before a notiflow can process triggers, it needs to be activated. 1. Go to the **Overview** tab 2. Toggle the notiflow to **Active** ## 6. Install the SDK Add the Notiflows SDK to your backend application. ```bash npm install @notiflows/node ``` ```bash pip install notiflows ``` ```bash gem install notiflows ``` ## 7. Trigger the Notiflow Now you can trigger your notiflow from your backend whenever you want to send a notification. ```typescript import Notiflows from '@notiflows/node'; const client = new Notiflows({ apiKey: process.env.NOTIFLOWS_API_KEY, secret: process.env.NOTIFLOWS_SECRET, }); await client.notiflows.run('welcome-email', { recipients: [{ external_id: 'user_123' }], data: { name: 'Jane' }, }); ``` ```python from notiflows import Notiflows import os client = Notiflows( api_key=os.environ["NOTIFLOWS_API_KEY"], secret=os.environ["NOTIFLOWS_SECRET"], ) client.notiflows.run( "welcome-email", recipients=[{"external_id": "user_123"}], data={"name": "Jane"}, ) ``` ```ruby require "notiflows" client = Notiflows::Client.new( api_key: ENV["NOTIFLOWS_API_KEY"], secret: ENV["NOTIFLOWS_SECRET"] ) client.notiflows.run("welcome-email", { recipients: [{ external_id: "user_123" }], data: { name: "Jane" } }) ``` ### Understanding the Parameters | Parameter | Description | |-----------|-------------| | `notiflow` | The identifier of the notiflow to trigger (e.g., `welcome-email`) | | `recipients` | An array of recipient objects, each with an `external_id` field | | `data` | (Optional) An object containing variables used in your notification templates | | `topic` | Alternative to `recipients` - a topic identifier to notify all subscribers | | `actor` | (Optional) The user performing the action, with an `external_id` field | Use either `recipients` or `topic`, not both. With `recipients`, you specify exactly who gets notified. With `topic`, all users subscribed to that topic receive the notification. ## 8. Verify Delivery After triggering your notiflow, you can verify the notification was sent: 1. Go to your notiflow's **Runs** tab to see execution history 2. Click on a run to see detailed delivery information for each recipient 3. Check the **Notifications** section in your project to see all sent notifications ## What's Next? Now that you've sent your first notification, explore more features: Send notifications via SMS, push, in-app, Slack, and more Manage user profiles and their notification preferences Add delays, batching, throttling, and multi-channel orchestration Display real-time notifications in your web application --- # What is Notiflows? > Learn what Notiflows is and what it can do for you Source: https://notiflows.com/docs/getting-started/what-is-notiflows --- title: What is Notiflows? description: Learn what Notiflows is and what it can do for you --- Notiflows is a notification orchestration platform for SaaS products. It lets you design notiflows and deliver notifications across multiple channels like email, SMS, push, in-app, chat, and webhooks. ## Why Notiflows? Building a notification system from scratch is complex. You need to handle: - Multiple delivery channels with different APIs - Template management and dynamic content - User preferences and opt-outs - Delivery tracking and reliability - Batching, throttling, and timing logic Notiflows handles all of this so you can focus on your product. ## Core Concepts ### Notiflows A notiflow defines how and when notifications are delivered. Each notiflow contains steps that control the flow: - **Trigger** - Entry point when you call the API - **Channel** - Send via email, SMS, push, or other channels - **Wait** - Delay before the next step - **Digest** - Group multiple events into one notification - **Throttle** - Limit how often a user receives notifications ### Channels & Providers Channels are the delivery methods (email, SMS, push, etc.). Providers are the services that deliver them (SendGrid, Twilio, APNs, etc.). Configure your providers once, then use them across all your notiflows. ### Templates Each channel step has a template that defines the notification content. Templates use Liquid syntax for dynamic content with access to recipient data, actor data, and custom payloads. ### User Preferences Let users control which notifications they receive and through which channels. Preferences are enforced automatically when notiflows are triggered. ## How It Works 1. **Design** - Create notiflows in the dashboard with a visual editor 2. **Configure** - Set up channels with your provider credentials 3. **Trigger** - Call the API when events happen in your app 4. **Deliver** - Notiflows handles routing, rendering, and delivery 5. **Track** - Monitor delivery status and user engagement ## Use Cases - Welcome emails and onboarding sequences - Transactional notifications (order confirmations, receipts) - Activity notifications (comments, mentions, follows) - Alerts and reminders - Marketing and engagement campaigns ## Next Steps Set up your first notiflow in minutes Learn more about notiflows and how they work --- # Overview > Learn how to use Notiflows Source: https://notiflows.com/docs/learn --- title: Overview description: Learn how to use Notiflows --- Guides and tutorials to help you get the most out of Notiflows. Learn how to create notiflows with steps, templates, and versioning --- # Channel Step > Send notifications through a configured channel Source: https://notiflows.com/docs/learn/building-notiflows/channel-step --- title: Channel Step description: Send notifications through a configured channel --- The channel step sends a notification to the recipient through a specific channel. Each channel step is linked to a channel you've configured in your project. ## Configuration When adding a channel step, you select: 1. **Channel** — Which configured channel to use (e.g., "Marketing Email", "Transactional SMS") 2. **Template** — The notification content for this step ## Supported Channel Types Channel steps can use any channel type you've configured: | Channel Type | Providers | Description | |--------------|-----------|-------------| | **Email** | Amazon SES, SendGrid, Resend, Mailgun, Postmark, MailerSend, SMTP | Send emails with rich HTML, visual editor, or plaintext | | **SMS** | Twilio | Send text messages | | **Mobile Push** | Apple APNs, Firebase FCM | Send push notifications to iOS and Android devices | | **Web Push** | Web Push (VAPID) | Send web push notifications | | **In-App** | Built-in | Display notifications in your app's notification center | | **Chat** | Slack, WhatsApp, Telegram, Discord | Send messages to chat platforms | | **Webhook** | HTTP endpoint | Send to any URL with custom headers and body | See [Channels & Providers](/docs/channels-providers) for setup instructions for each provider. ## Templates Each channel step has an associated template that defines the notification content. The template format depends on the channel type: | Channel Type | Template Format | |--------------|-----------------| | Email | Visual editor, HTML, or plaintext | | SMS | Plaintext | | Mobile Push | Title + body (plaintext) | | Web Push | Title + body (plaintext) | | In-App | Body + action URL (markdown) | | Chat | Markdown or JSON | | Webhook | URL + method + headers + body (JSON) | All templates support Liquid variables like `{{ recipient.first_name }}`, `{{ actor.email }}`, and `{{ data.order_id }}`. See [Templates](/docs/learn/building-notiflows/templates) for details on writing template content. ## Example A channel step configured for email might use a template like: ```liquid Subject: Your order has shipped! Hi {{ recipient.first_name }}, Great news! Your order #{{ data.order_id }} is on its way. Track your package: {{ data.tracking_url }} ``` A webhook step template might look like: ``` URL: https://api.example.com/events Method: POST Body: { "user_id": "{{ recipient.external_id }}", "event": "order_shipped", "order_id": "{{ data.order_id }}" } ``` ## Multiple Channel Steps A notiflow can have multiple channel steps to send notifications across different channels: ``` Trigger → Email → Push → End ``` Or use control steps to add delays between channels: ``` Trigger → Email → Wait (1 hour) → Push → End ``` Or use condition steps to route to different channels based on user preferences: ``` Trigger → Condition ├─ Prefers email → Email → End └─ Default → Push → End ``` ## Step Conditions You can add conditions to a channel step to control whether it executes. If the conditions aren't met, the step is skipped and execution continues to the next step. This is different from condition steps which route the entire flow into branches — see [Condition Step](/docs/learn/building-notiflows/condition-step). --- # Condition Step > Route notifications into different branches based on conditions Source: https://notiflows.com/docs/learn/building-notiflows/condition-step --- title: Condition Step description: Route notifications into different branches based on conditions --- The condition step evaluates rules against recipient data, actor data, or custom payload and routes each notification down the matching branch. This lets you send different notifications — or skip notifications entirely — based on who the recipient is or what triggered the flow. ## How It Works A condition step contains one or more **branches**. Each branch has a set of conditions that are evaluated at runtime. The notification follows the first branch whose conditions match, or falls through to the default branch if no conditions match. ``` Trigger → Condition ├─ Premium users → Email + Push → End ├─ Trial users → Email → End └─ Default → End ``` ## Configuration Each condition step has: - **Branches** — Named paths that notifications can follow (e.g., "Premium Users", "Trial Users") - **Conditions** — Rules on each branch that determine which notifications follow that path - **Default branch** — A catch-all branch for notifications that don't match any other branch's conditions ### Branch Conditions Each branch can have one or more condition groups. Within a group, conditions are combined with **AND** or **OR** logic. A condition consists of: | Field | Description | |-------|-------------| | Property | The data field to evaluate (e.g., `recipient.plan`, `data.amount`) | | Operator | How to compare the value | | Value | The expected value to compare against | ### Operators | Operator | Description | |----------|-------------| | Equal to | Exact match | | Not equal to | Does not match | | Greater than | Numeric comparison | | Less than | Numeric comparison | | Greater than or equal to | Numeric comparison | | Less than or equal to | Numeric comparison | | Contains | String contains substring | | Does not contain | String does not contain substring | | Starts with | String starts with prefix | | Ends with | String ends with suffix | | Is empty | Field is null or empty | | Is not empty | Field has a value | ## Use Cases **Notification preferences** Route based on the recipient's preferred channel: ``` Trigger → Condition ├─ Prefers email → Email → End ├─ Prefers SMS → SMS → End └─ Default → Push → End ``` **Plan-based content** Send different content to different user tiers: ``` Trigger → Condition ├─ Enterprise → Email (detailed report) → End ├─ Pro → Email (summary) → End └─ Free → Email (upgrade CTA) → End ``` **Locale-based routing** Send notifications in the recipient's language: ``` Trigger → Condition ├─ locale = "es" → Email (Spanish) → End ├─ locale = "fr" → Email (French) → End └─ Default → Email (English) → End ``` **Data-driven routing** Route based on the custom data payload: ``` Trigger → Condition ├─ data.priority = "urgent" → Push + SMS → End └─ Default → Email → End ``` ## Nesting Condition steps can appear anywhere in a flow, including inside another condition's branch. This lets you build complex routing logic: ``` Trigger → Condition (region) ├─ US → Condition (plan) │ ├─ Premium → Email + SMS → End │ └─ Default → Email → End └─ EU → Email (GDPR-compliant) → End ``` ## Condition Steps vs Step Conditions Notiflows has two different condition features: - **Condition steps** (this page) — Route the flow into branches. Every notification goes down exactly one branch. - **Step conditions** — A gate on an individual step. If the conditions aren't met, that single step is skipped and execution continues to the next step. Step conditions are configured per-step in the step settings panel. Condition steps are a dedicated step type that you add to the canvas. --- # Digest Step > Group multiple notifications together Source: https://notiflows.com/docs/learn/building-notiflows/digest-step --- title: Digest Step description: Group multiple notifications together --- The digest step collects multiple trigger events for the same recipient and groups them into a single notification. This prevents notification overload when many events occur in a short period. ## Configuration | Setting | Description | |---------|-------------| | Duration | The digest window duration (number) | | Unit | The time unit: **seconds**, **minutes**, **hours**, or **days** | | Digest Key | Optional property to group notifications by (e.g., `data.thread_id`) | The digest window opens when the first event arrives. When the window closes (after the configured duration), all collected events are sent as a single notification. ## Use Cases **Activity digests** Instead of sending a notification for every comment, digest them: ``` Trigger → Digest → Email → End ``` **Reducing notification fatigue** Digest rapid events to avoid overwhelming users: ``` Trigger → Digest → Push → End ``` ## Template Access When a digest window closes, templates have access to the collected events. Each item in `digest.items` contains: | Field | Description | |-------|-------------| | `data` | The original notification data payload | | `actor` | The actor who triggered the notification (`id`, `external_id`, `email`, `first_name`, `last_name`) | | `recipient` | The recipient of the notification (same fields as actor) | ```liquid You have {{ digest.total_items }} new notifications: {% for item in digest.items %} - {{ item.actor.first_name }} {{ item.actor.last_name }}: {{ item.data.message }} {% endfor %} ``` ## How It Works 1. First trigger event starts a new digest window 2. Subsequent triggers for the same recipient are added to the digest 3. When the digest window closes, execution continues to the next step 4. The next step receives all the digested data 5. A new digest window starts for the next set of triggers ## Digest Key Events are digested per recipient by default. Each user has their own digest that collects events independently. You can optionally set a **digest key** to group events by a specific property. For example, setting the digest key to `data.thread_id` will create separate digest windows for each thread — so a user subscribed to multiple threads gets one digest per thread. ## Digest vs Throttle | | Digest | Throttle | |-|--------|----------| | **Behavior** | Combines events into one notification | Drops excess notifications | | **Data** | All events included in the digest | Only the first notification is sent | | **Use when** | You want to aggregate (e.g., "5 new comments") | You want to limit (e.g., max one alert per hour) | See [Throttle Step](/docs/learn/building-notiflows/throttle-step) for more on throttling. --- # Overview > Learn how to build notiflows with Notiflows Source: https://notiflows.com/docs/learn/building-notiflows/overview --- title: Overview description: Learn how to build notiflows with Notiflows --- A notiflow defines how and when notifications are delivered to your users. Each notiflow consists of a series of steps that control the flow of notification delivery. ## Notiflow Structure Every notiflow contains: - **Trigger Step** — The entry point that starts the notiflow when run via API - **Channel Steps** — Send notifications through configured channels (email, SMS, push, etc.) - **Control Steps** — Modify flow behavior (wait, digest, throttle) - **Condition Steps** — Route notifications down different branches based on rules - **End Step** — Marks the completion of a notiflow path Steps are ordered sequentially by position. Condition steps create branches, allowing different recipients to follow different paths through the flow. ## Step Types | Step | Purpose | |------|---------| | [Trigger](/docs/learn/building-notiflows/trigger-step) | Entry point — receives recipients, actor, and data from the API | | [Channel](/docs/learn/building-notiflows/channel-step) | Send a notification via a configured channel | | [Condition](/docs/learn/building-notiflows/condition-step) | Route notifications into branches based on conditions | | [Wait](/docs/learn/building-notiflows/wait-step) | Delay execution for a duration | | [Digest](/docs/learn/building-notiflows/digest-step) | Group notifications together | | [Throttle](/docs/learn/building-notiflows/throttle-step) | Limit notification frequency | | End | Terminate a notiflow path | ## Building a Notiflow 1. **Create a notiflow** — Give it a name and description 2. **Add steps** — Drag steps onto the canvas from the toolbar 3. **Configure channels** — Select which channel to use for each channel step 4. **Design templates** — Create the notification content with dynamic variables 5. **Test** — Preview and test your notiflow before publishing 6. **Publish** — Make the notiflow live and ready to run ## Example Flows A simple order confirmation notiflow: ``` Trigger → Email → End ``` A follow-up reminder with a delay: ``` Trigger → Email → Wait (24 hours) → Push → End ``` A flow that routes based on user attributes: ``` Trigger → Condition ├─ Premium users → Email + SMS → End └─ Free users → Email → End ``` ## Versioning Notiflows use a versioning system to safely make changes without affecting live notifications. You edit a draft version, and when ready, publish it to make it live. See [Versioning](/docs/learn/building-notiflows/versioning) for details. ## Templates Each channel step has an associated template that defines the notification content. Templates use Liquid syntax for dynamic content. See [Templates](/docs/learn/building-notiflows/templates) for details. --- # Templates > Create dynamic notification content with Liquid templating Source: https://notiflows.com/docs/learn/building-notiflows/templates --- title: Templates description: Create dynamic notification content with Liquid templating --- Templates define the content of your notifications. Notiflows uses [Liquid](https://shopify.github.io/liquid/) templating to insert dynamic content into your notifications. ## Variable Contexts Three variable contexts are available in every template: | Context | Description | Example | |---------|-------------|---------| | `recipient.*` | Recipient user attributes | `{{ recipient.first_name }}` | | `actor.*` | User who triggered the notification | `{{ actor.email }}` | | `data.*` | Custom payload from the API call | `{{ data.order_id }}` | These contexts are populated from the data you pass when [running a notiflow](/docs/learn/building-notiflows/trigger-step). ## Basic Syntax Insert variables with double curly braces: ```liquid Hi {{ recipient.first_name }}, {{ actor.first_name }} left a comment on your post. ``` ## Filters Transform values with filters: ```liquid {{ recipient.first_name | capitalize }} {{ data.price | money }} {{ data.created_at | date: "%B %d, %Y" }} ``` ## Conditionals Use conditional logic: ```liquid {% if data.discount %} You saved {{ data.discount }}! {% endif %} {% if recipient.custom_fields.plan == "premium" %} As a premium member, you get early access. {% else %} Upgrade to premium for early access. {% endif %} ``` ## Loops Iterate over arrays: ```liquid Your items: {% for item in data.items %} - {{ item.name }}: {{ item.price }} {% endfor %} ``` ## Template Formats by Channel ### Email Email templates support three content types: - **Visual** — Drag-and-drop editor for rich HTML emails - **HTML** — Raw HTML with full control over layout - **Plaintext** — Simple text emails ```liquid Subject: Order #{{ data.order_id }} confirmed Hi {{ recipient.first_name }}, Thanks for your order! Here's what you purchased: {% for item in data.items %} - {{ item.name }} ({{ item.quantity }}x) {% endfor %} Total: {{ data.total }} ``` ### SMS SMS templates use plaintext only: ```liquid Hi {{ recipient.first_name }}, your order #{{ data.order_id }} has shipped! Track it: {{ data.tracking_url }} ``` ### Mobile Push Mobile push templates have a title and body field (both plaintext): ```liquid Title: New message from {{ actor.first_name }} Body: {{ data.message_preview }} ``` ### Web Push Web push templates have a title and body (both plaintext), plus optional icon, image, and action URL fields: | Field | Required | Example | |-------|----------|---------| | Title | Yes | `{{ actor.first_name }} commented on your post` | | Body | Yes | `{{ data.comment_preview }}` | | Icon URL | No | `https://yourapp.com/icon.png` | | Image URL | No | `{{ data.post_image_url }}` | | Action URL | No | `https://yourapp.com/posts/{{ data.post_id }}` | ### In-App In-app templates have a body (markdown) and an optional action URL: ```liquid Body: **{{ actor.first_name }}** commented on your post: "{{ data.comment_preview }}" Action URL: {{ data.post_url }} ``` ### Chat (Slack) Chat templates support markdown or JSON for rich formatting: **Markdown:** ```liquid *New order #{{ data.order_id }}* Customer: {{ actor.first_name }} {{ actor.last_name }} Total: {{ data.total }} ``` **JSON (Slack Block Kit):** ```json { "blocks": [ { "type": "section", "text": { "type": "mrkdwn", "text": "*Order #{{ data.order_id }}*\nCustomer: {{ actor.first_name }}" } } ] } ``` ## Digest Templates When using a [digest step](/docs/learn/building-notiflows/digest-step), templates have access to the collected events via the `digest` context: | Variable | Description | |----------|-------------| | `digest.total_items` | Number of events collected in the digest window | | `digest.items` | Array of collected events | | `digest.items[].data` | The original notification data payload | | `digest.items[].actor` | The actor who triggered the event (`id`, `external_id`, `email`, `first_name`, `last_name`) | | `digest.items[].recipient` | The recipient of the notification (same fields as actor) | ```liquid You have {{ digest.total_items }} new comments: {% for item in digest.items %} - {{ item.actor.first_name }}: {{ item.data.message }} {% endfor %} ``` --- # Throttle Step > Limit notification frequency for recipients Source: https://notiflows.com/docs/learn/building-notiflows/throttle-step --- title: Throttle Step description: Limit notification frequency for recipients --- The throttle step limits how often a recipient can receive notifications from a notiflow. If a notification would exceed the throttle limit, it is dropped. ## Configuration | Setting | Description | |---------|-------------| | Duration | The throttle window (number) | | Unit | The time unit: **seconds**, **minutes**, **hours**, or **days** | | Throttle Key | Optional property to throttle by (e.g., `data.project_id`) | ## Use Cases **Prevent notification spam** Limit promotional notifications to once per day: ``` Trigger → Throttle (1 day) → Email → End ``` If the notiflow is run multiple times for the same user within 24 hours, only the first notification is sent. **Rate limit alerts** Prevent alert fatigue by throttling system notifications: ``` Trigger → Throttle (1 hour) → Push → End ``` ## How It Works 1. When execution reaches a throttle step, it checks if a notification was already allowed through this step within the throttle window 2. **Within the window** — the notiflow stops and the notification is dropped 3. **Outside the window** — execution continues to the next step and a throttle event is recorded for future checks ## Throttle Key Notifications are throttled per recipient by default. Each user has their own throttle gate that operates independently. You can optionally set a **throttle key** to throttle by a specific property. For example, setting the throttle key to `data.project_id` will create separate throttle windows for each project — so a user working on multiple projects can receive one alert per project within the window, rather than one alert total. ``` User receives alerts for project_A and project_B: Without throttle key (1 hour window): project_A alert → ✓ allowed project_B alert → ✗ dropped (same recipient, within window) With throttle key = data.project_id (1 hour window): project_A alert → ✓ allowed project_B alert → ✓ allowed (different key, independent window) project_A alert → ✗ dropped (same key, within window) ``` When the throttle key property is missing from the notification data, it falls back to a default group — all notifications without the property are throttled together. ## Throttle vs Digest | | Throttle | Digest | |-|----------|--------| | **Behavior** | Drops excess notifications | Combines notifications into one | | **Data** | Only the first notification is sent | All events included in the digest | | **Use when** | You want to limit (e.g., max one alert per hour) | You want to aggregate (e.g., "5 new comments") | **Throttle example**: User gets one "new follower" push per hour — extras are dropped. **Digest example**: User gets one email listing all new followers from the past hour. See [Digest Step](/docs/learn/building-notiflows/digest-step) for more on digesting. ## Combining Steps You can combine throttle with other control steps: ``` Trigger → Digest → Throttle (4 hours) → Email → End ``` This digests events first, then ensures the user doesn't receive more than one digest notification every 4 hours. --- # Trigger Step > The entry point for your notiflow Source: https://notiflows.com/docs/learn/building-notiflows/trigger-step --- title: Trigger Step description: The entry point for your notiflow --- The trigger step is the starting point of every notiflow. When you run a notiflow via the API, execution begins at this step and flows through the subsequent steps. ## How It Works When you call the run endpoint, you provide: - **Recipients** — The users who will receive the notifications - **Actor** (optional) — The user who caused the notification (e.g., the commenter, the follower) - **Data** (optional) — Custom payload with dynamic content for templates - **Topic** (alternative) — Send to all users subscribed to a topic instead of listing recipients The trigger step receives this data and passes it to every subsequent step in the flow. ## Running via API Use the Admin API to run a notiflow: ```bash curl -X POST https://api.notiflows.com/admin/v1/notiflows/{notiflow_handle}/run \ -H "x-notiflows-api-key: pk_your_api_key" \ -H "x-notiflows-secret-key: sk_your_secret_key" \ -H "Content-Type: application/json" \ -d '{ "recipients": [ { "external_id": "user_123", "email": "jane@example.com", "first_name": "Jane" } ], "actor": { "external_id": "user_456", "first_name": "Alex" }, "data": { "order_id": "ORD-789", "total": "$99.00" } }' ``` ### Authentication The Admin API requires two headers: | Header | Description | |--------|-------------| | `x-notiflows-api-key` | Your project's public API key (starts with `pk_`) | | `x-notiflows-secret-key` | Your project's secret key (starts with `sk_`) | Find both keys in your Notiflows Dashboard under **Project Settings**. Your secret key grants full access to your project. Never expose it in client-side code, commit it to version control, or share it publicly. ### Request Body #### Recipients The `recipients` field is an array of user objects. Each object must include an `external_id` — the user ID from your system. You can also include user attributes that will be stored and available in templates: ```json { "recipients": [ { "external_id": "user_123", "email": "jane@example.com", "first_name": "Jane", "last_name": "Doe", "phone": "+15551234567", "avatar": "https://example.com/avatars/jane.jpg", "locale": "en-US", "timezone": "America/New_York", "custom_fields": { "plan": "premium", "company": "Acme Inc" } } ] } ``` | Field | Required | Description | |-------|----------|-------------| | `external_id` | Yes | Your system's user ID (string or integer) | | `email` | No | User's email address | | `first_name` | No | User's first name | | `last_name` | No | User's last name | | `phone` | No | Phone number in E.164 format (e.g., `+15551234567`) | | `avatar` | No | URL to user's avatar image (must be `https://`) | | `locale` | No | BCP 47 locale (e.g., `en-US`) | | `timezone` | No | IANA timezone (e.g., `America/New_York`) | | `custom_fields` | No | Arbitrary JSON object for additional user data | If a recipient with the given `external_id` doesn't exist yet, Notiflows creates the user automatically with the provided attributes. If they already exist, their attributes are updated. #### Actor The `actor` field is optional and uses the same structure as a recipient object. The actor represents the user who triggered the notification — for example, the person who commented on a post or followed another user. ```json { "actor": { "external_id": "user_456", "first_name": "Alex" } } ``` #### Data The `data` field is an optional JSON object containing any custom payload you want to use in templates: ```json { "data": { "order_id": "ORD-789", "total": "$99.00", "items": [ { "name": "Widget", "quantity": 2 } ] } } ``` #### Topic (Alternative to Recipients) Instead of listing individual recipients, you can send to all users subscribed to a topic: ```json { "topic": "order-updates", "data": { "order_id": "ORD-789" } } ``` You must provide either `recipients` or `topic`, but not both. ### Response A successful request returns `201 Created`: ```json { "notiflow_run_id": "01HQXYZ123456789ABCDEFGHIJ" } ``` ### Errors | Status | Description | |--------|-------------| | 404 | Notiflow not found | | 409 | Notiflow is not active or has no published version | | 422 | Validation failed (missing or invalid fields) | ### Prerequisites Before a notiflow can be run: 1. The notiflow must have a **published version** — see [Versioning](/docs/learn/building-notiflows/versioning) 2. The notiflow must be **active** (not deactivated) ## Data Context The data you pass when running becomes available in templates throughout the flow: | Context | Description | |---------|-------------| | `recipient.*` | Recipient user attributes (first_name, email, etc.) | | `actor.*` | Actor user attributes | | `data.*` | Custom payload you passed when running | See [Templates](/docs/learn/building-notiflows/templates) for how to use these variables. ## Connected Steps The trigger step connects to the first action step in your flow. This is typically a channel step, but could also be a control step like wait, digest, or a condition step for routing. --- # Versioning > Manage notiflow versions and publishing Source: https://notiflows.com/docs/learn/building-notiflows/versioning --- title: Versioning description: Manage notiflow versions and publishing --- Notiflows uses a versioning system that lets you safely make changes without affecting live notifications. You edit a draft, and when ready, publish it to make it live. Previous published versions are automatically archived. ## Version States Each version has one of three states: | State | Description | |-------|-------------| | **Draft** | The working version you're actively editing. Only one draft exists at a time. | | **Published** | The live version that executes when the notiflow is run. Immutable — cannot be edited. | | **Archived** | A previously published version that was replaced by a newer publish. Kept for history. | ## How Versioning Works When you trigger a notiflow, it always uses the **published** version. Your draft changes don't affect live notifications until you publish. ``` Create notiflow → Edit draft → Publish → Edit new draft → Publish → ... │ │ └─ archived └─ previous published → archived ``` ## Publishing When you publish a notiflow: 1. The draft is validated — steps, templates, and connections are checked for correctness 2. The current published version (if any) is moved to **archived** 3. The draft becomes the new **published** version 4. A new draft is created for future edits 5. All future runs use the newly published version 6. In-flight runs (already triggered) continue with their original version Publishing validates the flow structure before going live. If there are issues — like a channel step with no template, or a disconnected step — the publish will fail with specific error messages. ## Draft Changes While editing a draft, you can: - Add, remove, or reorder steps - Change step configurations (channels, wait durations, conditions, etc.) - Edit template content - Add or remove branches on condition steps None of these changes affect the published version until you explicitly publish. ## Activating and Deactivating Notiflows have an **active** toggle that is separate from versioning: | State | Behavior | |-------|----------| | **Active** | Can be run via the API. Uses the published version. | | **Inactive** | Cannot be run. API calls return a `409 Conflict` error. | Deactivating a notiflow is useful when you need to temporarily stop notifications without deleting the notiflow or losing your published version. A notiflow must be both **active** and have a **published version** to be runnable. If either condition is missing, the API returns an error. ## Best Practices - **Test before publishing** — Use the preview and test features to verify your changes work correctly before publishing. - **Publish intentionally** — Review all draft changes before publishing. Published versions are immutable. - **Use deactivation over deletion** — If you need to stop a notiflow temporarily, deactivate it rather than deleting it. You can reactivate it later without losing your configuration. --- # Wait Step > Delay notification delivery for a specified duration Source: https://notiflows.com/docs/learn/building-notiflows/wait-step --- title: Wait Step description: Delay notification delivery for a specified duration --- The wait step pauses notiflow execution for a specified duration before continuing to the next step. This is useful for sending follow-up notifications or spacing out a multi-channel sequence. ## Configuration | Setting | Description | |---------|-------------| | Duration | The amount of time to wait (number) | | Unit | The time unit: **seconds**, **minutes**, **hours**, or **days** | ## Use Cases **Follow-up reminders** Send an initial notification, then wait before sending a reminder: ``` Trigger → Email → Wait (24 hours) → Email (reminder) → End ``` **Delayed notifications** Wait before sending a notification to give users time to complete an action: ``` Trigger → Wait (1 hour) → Push → End ``` **Multi-channel sequences** Space out notifications across channels: ``` Trigger → Email → Wait (2 hours) → SMS → Wait (1 day) → Push → End ``` ## How It Works When execution reaches a wait step: 1. The notiflow pauses at the wait step 2. A scheduled job is created for the specified duration 3. After the duration passes, execution resumes from the next step The notiflow state is preserved during the wait period — all context data (recipient, actor, data) remains available for subsequent steps. --- # Overview > How Notiflows secures your integration across APIs, keys, and origins Source: https://notiflows.com/docs/learn/security --- title: Overview description: How Notiflows secures your integration across APIs, keys, and origins --- # Security Notiflows uses a layered security model to protect your integration. Each layer serves a different purpose: | Layer | What it does | Where to configure | |-------|-------------|-------------------| | [API Keys](/docs/learn/security/api-keys) | Identify your project and authenticate server-to-server requests | **Settings** → **API Keys** | | [Client Authentication](/docs/learn/security/client-authentication) | Verify user identity in browser requests via signed JWTs | **Settings** → **API Keys** → **Client Authentication** | | [Allowed Origins](/docs/learn/security/allowed-origins) | Restrict which domains can make browser requests | **Settings** → **API Keys** → **Allowed Origins** | ## Which APIs use what | API | Authentication | Learn more | |-----|---------------|------------| | **Admin API** | API key + secret key | [Admin API Reference](/docs/api/admin) | | **User API** | API key + user token (or user ID in dev mode) | [User API Reference](/docs/api/user) | ## Production checklist Before going live, make sure you have: - **Security Mode enabled** — so client requests require signed JWTs, not plain user IDs - **Allowed origins configured** — so only your domains can call the User API from the browser - **Secret key stored securely** — never in client-side code or version control - **Short-lived user tokens** — set JWT expiry to 1 hour or less --- # Allowed Origins > Control which domains can make browser requests to the Notiflows User API Source: https://notiflows.com/docs/learn/security/allowed-origins --- title: Allowed Origins description: Control which domains can make browser requests to the Notiflows User API --- # Allowed Origins Allowed origins control which domains can make browser requests to the Notiflows User API. When you embed the in-app inbox widget or use a client-side SDK, the browser sends an `Origin` header with each request. Notiflows checks this header against your project's allowed origins list and blocks requests from unlisted domains. If no allowed origins are configured, all browser requests to the User API will be blocked. You must add at least one origin before your client-side integration will work. ## How It Works 1. A user visits your app at `https://app.example.com` 2. Your frontend makes a request to the Notiflows User API 3. The browser includes `Origin: https://app.example.com` in the request 4. Notiflows checks the origin against your project's allowed origins 5. If the origin matches, the request proceeds. Otherwise, the browser blocks the response. Non-browser requests (server-side SDKs, cURL, etc.) don't send an `Origin` header and are not affected by this setting. ## Configuring Allowed Origins Navigate to your project settings in the Notiflows dashboard: 1. Go to **Projects** → **Settings** → **API Keys** 2. Find the **Allowed Origins** section 3. Enter an origin (e.g., `https://app.example.com`) and click the **+** button 4. Click **Save** to apply You can add multiple origins for different environments (staging, production, etc.). ## Origin Format An origin is the combination of scheme, hostname, and port. It must not include a path, query string, or fragment. ### Valid Origins | Origin | Notes | |--------|-------| | `https://app.example.com` | Production domain (HTTPS required) | | `https://staging.example.com` | Staging domain | | `http://localhost:3000` | Local development (HTTP allowed) | | `http://localhost:5173` | Vite dev server | | `http://127.0.0.1:8000` | Local IP with port | ### Invalid Origins | Origin | Reason | |--------|--------| | `example.com` | Missing protocol (`https://`) | | `https://app.example.com/dashboard` | Contains a path | | `http://app.example.com` | HTTP not allowed for non-localhost domains | ### Rules - **HTTPS required** for all production domains - **HTTP allowed** only for `localhost` and `127.0.0.1` (development) - **No paths, query strings, or fragments** — origin only - **Ports are part of the origin** — `https://example.com` and `https://example.com:8443` are different origins ## Development Setup For local development, add your dev server's origin: ``` http://localhost:3000 http://localhost:5173 ``` These origins use HTTP, which is only permitted for localhost and 127.0.0.1 addresses. Remember to add your production domain before deploying. A common mistake is to only configure localhost origins and then wonder why the widget doesn't work in production. ## Troubleshooting ### CORS errors in the browser console If you see errors like `Access to fetch has been blocked by CORS policy`, check that: 1. Your domain is listed in **Settings** → **API Keys** → **Allowed Origins** 2. The origin matches exactly — including protocol and port 3. You've clicked **Save** after adding the origin ### Widget works locally but not in production Your production domain is likely missing from allowed origins. Add `https://yourdomain.com` (with HTTPS) and save. ### Requests work from Postman/cURL but not the browser This is expected. Non-browser tools don't enforce CORS. Add your frontend's origin to the allowed origins list. ## Next Steps - [Client Authentication](/docs/learn/security/client-authentication) — Signing keys and security modes - [JavaScript SDK](/docs/sdks/client-side/javascript) — Client-side notification integration - [React SDK](/docs/sdks/client-side/react) — Pre-built React components and hooks --- # API Keys > Understand the different keys used to authenticate with Notiflows APIs Source: https://notiflows.com/docs/learn/security/api-keys --- title: API Keys description: Understand the different keys used to authenticate with Notiflows APIs --- # API Keys Each Notiflows project has a set of keys for authenticating with different APIs. You can find and manage them in **Settings** → **API Keys**. ## Key Types ### Public API Key (`pk_*`) The public API key identifies your project. It is safe to include in client-side code — it cannot be used alone to perform sensitive actions. - **Prefix:** `pk_` - **Used by:** User API (browser/mobile), Admin API (with secret key) - **Header:** `x-notiflows-api-key` ### Secret Key (`sk_*`) The secret key authenticates server-to-server requests to the Admin API. It must be kept confidential. - **Prefix:** `sk_` - **Used by:** Admin API only - **Header:** `x-notiflows-secret-key` Never expose your secret key in client-side code, public repositories, or browser network requests. Use environment variables or a secrets manager. ### Application Signing Key (RSA) The signing key is an RSA key pair used for [client authentication](/docs/learn/security/client-authentication). Your backend signs JWTs with the private key; Notiflows verifies them with the public key. - **Algorithm:** RS256 (RSA 2048-bit) - **Private key:** Shown only once during generation — store it securely - **Public key:** Stored by Notiflows ## Regenerating Keys You can regenerate any key from the dashboard: 1. Go to **Settings** → **API Keys** 2. Click the regenerate button next to the key Regenerating a key immediately invalidates the old one. Update your backend before or immediately after regenerating to avoid downtime. Regenerating the signing key invalidates all existing user tokens. ## Next Steps - [Admin API Reference](/docs/api/admin) — Using API key + secret key for server-to-server requests - [User API Authentication](/docs/api/user/authentication) — Using API key + user tokens for client-side requests - [Client Authentication](/docs/learn/security/client-authentication) — Set up JWT signing for the User API - [Allowed Origins](/docs/learn/security/allowed-origins) — Restrict which domains can make browser requests --- # Client Authentication > Secure client-side API requests with signing keys and security modes Source: https://notiflows.com/docs/learn/security/client-authentication --- title: Client Authentication description: Secure client-side API requests with signing keys and security modes --- # Client Authentication The User API is designed for client-side use — it powers in-app inboxes, notification feeds, and preference screens. Because it runs in the browser, securing it requires two layers: **authentication** (proving who the user is) and **allowed origins** (restricting which domains can make requests). This page covers authentication. See [Allowed Origins](/docs/learn/security/allowed-origins) for domain restrictions. ## Security Modes Notiflows supports two security modes for the User API: ### Secure Mode (Recommended) When **Security Mode** is enabled (default), each request requires: - `x-notiflows-api-key` — your project's public API key - `x-notiflows-user-key` — a JWT signed with your Application Signing Key (RS256) Your backend signs JWTs with the **private key**. Notiflows verifies them with the **public key** stored in your project. The private key never leaves your infrastructure. ### Development Mode When Security Mode is disabled, each request requires: - `x-notiflows-api-key` — your project's public API key - `x-notiflows-user-id` — the user's external ID (no signing) Development mode is NOT safe for production. Anyone with your API key can impersonate any user. Always enable Security Mode for production deployments. ## Setting Up Signing Keys 1. Go to **Projects** → **Settings** → **API Keys** 2. Find the **Client Authentication** section 3. Click **Generate signing key** 4. **Save the private key** — it is only shown once The private key is provided in two formats: - **Base64-encoded PEM** — single-line, ideal for environment variables - **Raw PEM** — standard format for file storage Security Mode is enabled by default. If disabled, re-enable it in the same section. ## Generating User Tokens Your backend generates a JWT for each user. The token payload: ```json { "sub": "user_external_id", "iat": 1608600116, "exp": 1608603716 } ``` | Claim | Required | Description | |-------|----------|-------------| | `sub` | Yes | The user's external ID (must match a user in Notiflows) | | `iat` | Recommended | Unix timestamp when the token was issued | | `exp` | Recommended | Unix timestamp when the token expires | For code examples in Node.js, Python, and Ruby, see the [Authentication API reference](/docs/api/user/authentication#code-examples). ## Best Practices - **Short-lived tokens** — set expiry to 1 hour or less and implement token refresh - **Store keys securely** — use environment variables or a secrets manager, never client-side code or version control - **Rotate keys** when compromised — regenerating a key invalidates all existing tokens immediately ## Next Steps - [Allowed Origins](/docs/learn/security/allowed-origins) — Restrict which domains can call the User API - [Authentication API Reference](/docs/api/user/authentication) — Full details with code examples --- # Overview > Notiflows SDKs for server-side and client-side integration Source: https://notiflows.com/docs/sdks --- title: Overview description: Notiflows SDKs for server-side and client-side integration --- Notiflows provides SDKs to help you integrate notifications into your application. There are two types of SDKs: - **Server-side SDKs** - Trigger notiflows and manage users from your backend - **Client-side SDKs** - Display notifications and manage preferences in your frontend ## Server-side SDKs Server-side SDKs authenticate using your **Secret API Key** and are used to: - Trigger notiflows to send notifications - Create and manage users - Manage topic subscriptions - Query notifications and delivery status Official SDK for Node.js applications Official SDK for Python applications Official SDK for Ruby applications Official SDK for Go applications (coming soon) Official SDK for PHP applications (coming soon) Official SDK for Java applications (coming soon) Official SDK for .NET applications (coming soon) ## Client-side SDKs Client-side SDKs authenticate using a **User Key** (generated per-user) and are used to: - Display in-app notification feeds - Mark notifications as read, seen, or archived - Manage user notification preferences - Subscribe to real-time notification updates Core client for any JavaScript application React components and hooks for notification UI Native SDK for iOS, iPadOS, macOS, and watchOS (coming soon) Native SDK for Android (coming soon) ## Authentication ### Server-side Authentication Server-side SDKs use your project's **API Key** and **Secret Key** for authentication. You can find them in your project settings under **API Keys**. ```typescript import Notiflows from '@notiflows/node'; const client = new Notiflows({ apiKey: process.env.NOTIFLOWS_API_KEY, secret: process.env.NOTIFLOWS_SECRET, }); ``` Never expose your Secret API Key in client-side code. It should only be used in your backend. ### Client-side Authentication Client-side SDKs require a **User Key** that you generate on your backend for each authenticated user. This ensures users can only access their own notifications. See [Generating User Tokens](/docs/api/user/authentication#generating-user-tokens) for detailed instructions on how to generate user keys from your backend. ## API Reference All SDKs interact with the Notiflows API. For detailed endpoint documentation, see: - [Admin API Reference](/docs/api/admin) - Server-to-server API - [User API Reference](/docs/api/user) - Client-side API --- # JavaScript > Notiflows JavaScript SDK for client-side notification integration Source: https://notiflows.com/docs/sdks/client-side/javascript --- title: JavaScript description: Notiflows JavaScript SDK for client-side notification integration --- The JavaScript SDK (`@notiflows/client`) provides a client for interacting with the Notiflows User API. Use it to fetch notifications, manage notification state, and subscribe to real-time updates. For React applications, we recommend using the [React SDK](/docs/sdks/client-side/react) which provides hooks and pre-built components on top of this client. ## Installation ```bash npm install @notiflows/client # or pnpm add @notiflows/client # or yarn add @notiflows/client ``` ## Quick Start ```typescript import { Notiflows } from '@notiflows/client'; // Initialize the client const client = new Notiflows({ apiKey: 'pk_your_public_key', userId: 'user_123', userKey: 'jwt_token_from_backend', }); // Get the notification feed const feed = client.feed({ channelId: 'your-channel-id' }); // Fetch notifications const collection = await feed.getEntries(); console.log(collection.items, collection.total_unread); // Subscribe to real-time updates feed.onDelivery = (entry) => { console.log('New notification:', entry); }; feed.subscribeToRealtimeNotifications(); // Mark as read await feed.markAsRead('entry_id'); ``` ## Configuration ```typescript const client = new Notiflows({ // Required apiKey: string, // Your public API key (starts with pk_) userId: string, // The user's external ID userKey: string, // JWT token signed with your signing key // Optional apiUrl?: string, // Custom API URL (default: https://api.notiflows.com/user/v1) wsUrl?: string, // Custom WebSocket URL (default: wss://api.notiflows.com/ws/v1) }); ``` The `userKey` must be generated on your backend using your Application Signing Key. Never expose your signing key in client-side code. See [Generating User Tokens](/docs/api/user/authentication#generating-user-tokens) for instructions. ## Feed The Feed resource manages notification entries for a specific channel. ### Creating a Feed Instance ```typescript const feed = client.feed({ channelId: 'your-in-app-channel-id', }); ``` ### Fetching Entries ```typescript import { FeedEntryStatus } from '@notiflows/client'; // Get all notifications const collection = await feed.getEntries(); // Access the data const { items, total_unread, total_unseen } = collection; // With filters const unreadOnly = await feed.getEntries({ status: FeedEntryStatus.Unread, limit: 20, }); // With pagination const collection = await feed.getEntries({ limit: 10 }); if (collection.has_more_after) { const nextPage = await feed.getEntries({ limit: 10, after: collection.after, }); } // Filter by notiflow handle const orderNotifs = await feed.getEntries({ notiflow: 'order-updates', }); // Filter by topic const topicNotifs = await feed.getEntries({ topic: 'order:123', }); // Include archived entries const withArchived = await feed.getEntries({ archived: true, }); ``` ### Feed Settings Fetch feed settings (e.g. whether branding is required): ```typescript const settings = await feed.getSettings(); console.log(settings.branding_required); // true on free plan ``` ### Real-time Updates Subscribe to receive notifications as they're delivered: ```typescript // Set up handlers before subscribing feed.onDelivery = (entry) => { console.log('New notification:', entry); // Add to your UI, play a sound, etc. }; // Start listening for real-time updates feed.subscribeToRealtimeNotifications(); // Stop listening when done feed.stop(); ``` ### Updating Entry State Mark notifications as seen, read, clicked, or archived: ```typescript // Single entry updates await feed.markAsSeen(entryId); await feed.markAsRead(entryId); await feed.markAsClicked(entryId); await feed.markAsArchived(entryId); await feed.markAsUnarchived(entryId); // Batch updates await feed.batchMarkAsSeen([entryId1, entryId2]); await feed.batchMarkAsRead([entryId1, entryId2]); await feed.batchMarkAsClicked([entryId1, entryId2]); await feed.batchMarkAsArchived([entryId1, entryId2]); await feed.batchMarkAsUnarchived([entryId1, entryId2]); // Generic update with multiple fields await feed.updateEntry(entryId, { read: true, seen: true, }); await feed.batchUpdateEntries([entryId1, entryId2], { archived: true, }); ``` ## User Preferences Manage user notification preferences: ```typescript const preferences = client.userPreferences(); // Get current preferences const prefs = await preferences.get(); console.log(prefs.notiflows); // Per-notiflow preferences // Update preferences await preferences.update({ notiflows: { 'marketing-emails': { enabled: false, }, }, }); ``` ## Types ### FeedEntry ```typescript interface FeedEntry { id: string; notiflow_handle: string; data: { body: string; action_type: 'default' | 'single' | 'multi'; action_url?: string; primary_action?: { label: string; url: string }; secondary_action?: { label: string; url: string }; }; actor?: { id: string; external_id: string; first_name?: string; last_name?: string; avatar?: string; email?: string; }; topic?: string; status: FeedEntryStatus; sent_at?: string; seen_at?: string; read_at?: string; clicked_at?: string; archived_at?: string; created_at: string; } ``` ### FeedEntryCollection ```typescript interface FeedEntryCollection { items: FeedEntry[]; total_unread: number; total_unseen: number; has_more_after: boolean; after: string | null; } ``` ### FeedSettings ```typescript interface FeedSettings { branding_required: boolean; } ``` ### FeedEntryStatus ```typescript enum FeedEntryStatus { Unseen = 'unseen', Seen = 'seen', Unread = 'unread', Read = 'read', Archived = 'archived', } ``` ### Preferences ```typescript interface Preferences { notiflows: { [notiflowHandle: string]: { // keyed by notiflow handle (e.g. "order-updates") name: string; enabled: boolean; }; }; } ``` ## Error Handling The SDK provides typed errors for different API responses: ```typescript import { isApiError, BadRequestError, UnauthenticatedError, ForbiddenError, NotFoundError, RateLimitedError, ValidationFailedError, ConflictError, InternalError, ServiceUnavailableError, } from '@notiflows/client'; try { await feed.getEntries(); } catch (error) { if (isApiError(error)) { console.log(error.code); // Error code console.log(error.message); // Error message console.log(error.details); // Additional details if (error instanceof UnauthenticatedError) { // Redirect to login or refresh token } else if (error instanceof RateLimitedError) { // Back off and retry } else if (error instanceof ValidationFailedError) { // Check error.details for field-level errors } } } ``` ### Error Types | Error | HTTP Status | Description | |-------|-------------|-------------| | `BadRequestError` | 400 | Invalid request parameters | | `UnauthenticatedError` | 401 | Invalid or missing authentication | | `ForbiddenError` | 403 | Insufficient permissions | | `NotFoundError` | 404 | Resource not found | | `ConflictError` | 409 | Resource state conflict | | `ValidationFailedError` | 422 | Validation errors on request body | | `RateLimitedError` | 429 | Too many requests | | `InternalError` | 500 | Server error | | `ServiceUnavailableError` | 503 | Service temporarily unavailable | ## TypeScript The SDK is written in TypeScript and exports all types: ```typescript import type { FeedEntry, FeedEntryCollection, FeedEntryStatus, FeedSettings, GetEntriesParams, EntryStateUpdate, Preferences, NotiflowsOptions, } from '@notiflows/client'; ``` ## Allowed Origins Before using the SDK in a browser, you must add your domain to the project's allowed origins list. Without this, the browser will block all API requests due to CORS. See [Allowed Origins](/docs/learn/security/allowed-origins) for setup instructions. ## Browser Support The SDK supports all modern browsers (ES2022+) and Node.js 18+. For older browsers, you may need to polyfill: - `Promise` - `fetch` (if using Node.js < 18) ## Next Steps - [React SDK](/docs/sdks/client-side/react) - Pre-built components and hooks for React - [Authentication](/docs/api/user/authentication) - Learn about user token generation - [API Reference](/docs/api/user) - Full User API documentation --- # Kotlin > Notiflows Kotlin SDK for Android Source: https://notiflows.com/docs/sdks/client-side/kotlin --- title: Kotlin description: Notiflows Kotlin SDK for Android --- The Kotlin SDK for native Android integration is coming soon. ## Planned Features - Native Kotlin API for Android - In-app notification feed management - Real-time updates via WebSocket - User preference management - Jetpack Compose components --- # React > Notiflows React SDK with hooks and components for notification UI Source: https://notiflows.com/docs/sdks/client-side/react --- title: React description: Notiflows React SDK with hooks and components for notification UI --- The React SDK (`@notiflows/react`) provides React hooks and pre-built components for displaying notifications in your application. It's built on top of the [JavaScript SDK](/docs/sdks/client-side/javascript). ## Installation ```bash npm install @notiflows/react # or pnpm add @notiflows/react # or yarn add @notiflows/react ``` ## Quick Start Import the styles and wrap your application with `NotiflowsProvider`: ```tsx import '@notiflows/react/styles.css'; import { NotiflowsProvider, FeedRoot, FeedTrigger, FeedContent, } from '@notiflows/react'; function App() { return (
); } function Header() { return (
); } ``` That's it! You now have a fully functional notification bell with a popover feed. ## Styles ### Default Styles The SDK ships with a CSS file that provides ready-to-use styling: ```tsx import '@notiflows/react/styles.css'; ``` You must import the styles for the pre-built components to render correctly. Import it once in your app's entry point. ### Customization with CSS Variables Override CSS variables to match your brand: ```css :root { /* Colors */ --nf-primary: #0066cc; --nf-primary-foreground: #ffffff; --nf-background: #ffffff; --nf-foreground: #0f0f0f; --nf-muted: #f5f5f5; --nf-muted-foreground: #737373; --nf-border: #e5e5e5; --nf-popover: #ffffff; --nf-popover-foreground: #0f0f0f; /* Typography */ --nf-font-family: system-ui, sans-serif; --nf-font-size-xs: 0.75rem; --nf-font-size-sm: 0.875rem; --nf-font-size-base: 1rem; /* Spacing & Radius */ --nf-radius: 0.5rem; --nf-radius-sm: 0.25rem; /* Shadows */ --nf-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1); --nf-shadow-md: 0 10px 15px -3px rgb(0 0 0 / 0.1); /* Special */ --nf-unread-indicator: #0891b2; --nf-badge-bg: #ef4444; --nf-badge-text: #ffffff; } ``` ### Dark Mode Add the `dark` class to a parent element to enable dark mode: ```html ``` The SDK includes dark mode styles that are activated when a parent element has the `dark` class. ### Custom Classes All components accept a `className` prop for additional styling: ```tsx ``` ## Provider The `NotiflowsProvider` initializes the client and provides context to all child components. ```tsx {children} ``` The `userKey` must be generated on your backend using your Application Signing Key. Never expose your signing key in client-side code. See [Generating User Tokens](/docs/api/user/authentication#generating-user-tokens) for instructions. ## Components ### Feed Components The feed components create a popover-based notification center: ```tsx import { FeedRoot, FeedTrigger, FeedContent, FeedPanel, BellButton, } from '@notiflows/react'; // Minimal setup - uses defaults {/* Renders BellButton by default */} {/* Renders FeedPanel by default */} // Explicit setup ``` #### FeedRoot Wrapper component that manages the popover state. Wraps Radix UI Popover. ```tsx {children} ``` #### FeedTrigger Button that opens the notification popover. Renders `BellButton` by default. ```tsx // Default // Custom trigger ``` #### FeedContent Popover content container. Renders `FeedPanel` by default. ```tsx // Default // With custom alignment // With custom content ``` #### FeedPanel The main notification panel with tabs (All, Unread, Read) and settings. ```tsx ``` ### BellButton Bell icon button with unread badge: ```tsx import { BellButton, Badge } from '@notiflows/react'; // Standalone usage // Just the badge ``` ### Notification Individual notification item component: ```tsx import { Notification } from '@notiflows/react'; // Default rendering // With custom action handler { // Custom navigation logic router.push(url); }} /> ``` ### MarkAllAsRead Button to mark all notifications as read: ```tsx import { MarkAllAsRead } from '@notiflows/react'; markAllAsRead()}> Mark all as read ``` ### MarkAsArchived Archive button for individual notifications: ```tsx import { MarkAsArchived } from '@notiflows/react'; ``` ### Preferences Preferences panel with toggles for each notiflow: ```tsx import { Preferences } from '@notiflows/react'; ``` ### Avatar User avatar with fallback: ```tsx import { Avatar } from '@notiflows/react'; ``` ## Hooks ### useNotiflows Access the Notiflows client and feed client: ```tsx import { useNotiflows } from '@notiflows/react'; function MyComponent() { const { client, feedClient } = useNotiflows(); // Direct API access const handleGetPrefs = async () => { const prefs = await client.userPreferences().get(); console.log(prefs); }; } ``` ### useFeed Access the notification feed with automatic real-time updates: ```tsx import { useFeed } from '@notiflows/react'; function MyComponent() { const { entries, // FeedEntry[] - notification items totalUnread, // number - unread count totalUnseen, // number - unseen count lastPage, // pagination info isLoading, // boolean - initial load isLoadingMore, // boolean - loading more items error, // NotiflowsApiError | null loadMore, // () => Promise clearError, // () => void retry, // () => void } = useFeed(); // With options const { entries } = useFeed({ status: FeedEntryStatus.Unread, notiflow: 'order-updates', // filter by notiflow handle topic: 'order:123', // filter by topic archived: true, // include archived entries limit: 20, }); return (

{totalUnread} unread notifications

{entries.map(entry => (
{entry.data.body}
))} {lastPage?.has_more_after && ( )}
); } ``` ### useNotificationStatus Update notification states: ```tsx import { useNotificationStatus } from '@notiflows/react'; function MyComponent() { const { markAsSeen, // (entryId: string) => Promise markAsRead, // (entryId: string) => Promise markAsClicked, // (entryId: string) => Promise markAsArchived, // (entryId: string) => Promise markAsUnarchived, // (entryId: string) => Promise markAllAsRead, // () => Promise batchMarkAsRead, // (entryIds: string[]) => Promise batchMarkAsArchived, // (entryIds: string[]) => Promise } = useNotificationStatus(); const handleNotificationClick = async (entry: FeedEntry) => { if (!entry.read_at) { await markAsRead(entry.id); } if (entry.data.action_type === 'default' && entry.data.action_url) { window.location.href = entry.data.action_url; } }; } ``` ### usePreferences Manage user notification preferences: ```tsx import { usePreferences } from '@notiflows/react'; function PreferencesPanel() { const { preferences, // Preferences | null isLoading, // boolean updateNotiflowPreferences, // (notiflowHandle, prefs) => Promise } = usePreferences(); if (isLoading) return ; return (
{Object.entries(preferences?.notiflows || {}).map(([handle, pref]) => ( ))}
); } ``` ## Building Custom UI Use the hooks to build completely custom notification UIs: ```tsx import { useFeed, useNotificationStatus } from '@notiflows/react'; function CustomNotificationCenter() { const { entries, totalUnread, isLoading, loadMore } = useFeed(); const { markAsRead, markAllAsRead } = useNotificationStatus(); if (isLoading) { return
Loading...
; } return (

Notifications

{totalUnread}
    {entries.map(entry => (
  • markAsRead(entry.id)} > {entry.actor && ( {entry.actor.first_name} )}

    {entry.data.body}

  • ))}
); } ``` ## TypeScript The SDK is fully typed. Import types as needed: ```tsx import type { FeedEntry, FeedEntryCollection, FeedEntryStatus, Preferences, NotiflowsApiError, } from '@notiflows/react'; // Re-exported from @notiflows/client import { isApiError, BadRequestError, UnauthenticatedError, NotFoundError, } from '@notiflows/react'; ``` ## Error Handling Handle errors from hooks: ```tsx import { useFeed, isApiError } from '@notiflows/react'; function NotificationList() { const { entries, error, retry, clearError } = useFeed(); if (error) { return (

Failed to load notifications

{isApiError(error) &&

{error.message}

}
); } return (
    {entries.map(entry => (
  • {entry.data.body}
  • ))}
); } ``` ## Next.js Integration ### App Router Use the `'use client'` directive for components that use the SDK: ```tsx // components/notifications.tsx 'use client'; import '@notiflows/react/styles.css'; import { NotiflowsProvider, FeedRoot, FeedTrigger, FeedContent, } from '@notiflows/react'; interface NotificationsProps { userKey: string; userId: string; } export function Notifications({ userKey, userId }: NotificationsProps) { return ( ); } ``` ```tsx // app/layout.tsx import { Notifications } from '@/components/notifications'; import { auth, generateUserKey } from '@/lib/auth'; export default async function RootLayout({ children }) { const session = await auth(); // Generate user key on the server const userKey = await generateUserKey(session.user.id); return ( {children} ); } ``` ### Pages Router ```tsx // pages/_app.tsx import '@notiflows/react/styles.css'; // components/notifications.tsx import { NotiflowsProvider, FeedRoot, FeedTrigger, FeedContent, } from '@notiflows/react'; export function Notifications({ userKey, userId }) { return ( ); } ``` ## Allowed Origins Before using the SDK in a browser, you must add your domain to the project's allowed origins list. Without this, the browser will block all API requests due to CORS. See [Allowed Origins](/docs/learn/security/allowed-origins) for setup instructions. ## Browser Support The SDK supports all modern browsers (ES2022+): - Chrome 94+ - Firefox 93+ - Safari 15+ - Edge 94+ ## Next Steps - [JavaScript SDK](/docs/sdks/client-side/javascript) - Lower-level client for custom integrations - [Authentication](/docs/api/user/authentication) - Learn about user token generation - [API Reference](/docs/api/user) - Full User API documentation --- # Swift > Notiflows Swift SDK for iOS, iPadOS, macOS, and watchOS Source: https://notiflows.com/docs/sdks/client-side/swift --- title: Swift description: Notiflows Swift SDK for iOS, iPadOS, macOS, and watchOS --- The Swift SDK for native Apple platform integration is coming soon. ## Planned Features - Native Swift API for iOS, iPadOS, macOS, and watchOS - In-app notification feed management - Real-time updates via WebSocket - User preference management - SwiftUI components --- # .NET > Notiflows .NET SDK for server-side integration Source: https://notiflows.com/docs/sdks/server-side/dotnet --- title: .NET description: Notiflows .NET SDK for server-side integration --- The .NET SDK for server-side integration is coming soon. ## Planned Features - Trigger notiflows from your .NET backend - User management and preferences - Topic subscriptions - Notification and delivery tracking --- # Go > Notiflows Go SDK for server-side integration Source: https://notiflows.com/docs/sdks/server-side/go --- title: Go description: Notiflows Go SDK for server-side integration --- The Go SDK for server-side integration is coming soon. ## Planned Features - Trigger notiflows from your Go backend - User management and preferences - Topic subscriptions - Notification and delivery tracking --- # Java > Notiflows Java SDK for server-side integration Source: https://notiflows.com/docs/sdks/server-side/java --- title: Java description: Notiflows Java SDK for server-side integration --- The Java SDK for server-side integration is coming soon. ## Planned Features - Trigger notiflows from your Java backend - User management and preferences - Topic subscriptions - Notification and delivery tracking --- # Node.js > Notiflows Node.js SDK for server-side integration Source: https://notiflows.com/docs/sdks/server-side/node --- title: Node.js description: Notiflows Node.js SDK for server-side integration --- The Node.js SDK (`@notiflows/node`) allows you to trigger notiflows, manage users, and interact with the Notiflows Admin API from your Node.js backend. ## Installation ```bash npm install @notiflows/node ``` ## Quick Start ```typescript import Notiflows from '@notiflows/node'; const client = new Notiflows({ apiKey: process.env.NOTIFLOWS_API_KEY, secret: process.env.NOTIFLOWS_SECRET, }); // Trigger a notiflow await client.notiflows.run('welcome-email', { data: { name: 'Jane' }, recipients: [{ external_id: 'user_123' }], }); ``` ## Configuration ```typescript const client = new Notiflows({ apiKey: 'pk_your_api_key', secret: 'sk_your_secret_key', }); ``` Both `apiKey` and `secret` are required. Find them in your project's **Settings > API Keys**. ## Triggering Notiflows ```typescript // Send to specific recipients await client.notiflows.run('order-shipped', { data: { order_id: 'order_789', tracking_url: 'https://example.com/track/789', }, recipients: [ { external_id: 'user_123' }, { external_id: 'user_456' }, ], }); // Send to all subscribers of a topic await client.notiflows.run('new-comment', { topic: 'post_123', data: { commenter: 'Jane', comment: 'Great post!' }, actor: { external_id: 'user_456' }, }); ``` ### Parameters | Parameter | Type | Description | |-----------|------|-------------| | `notiflowHandle` | string | The notiflow identifier (first argument) | | `recipients` | array | Array of recipient objects with `external_id` | | `topic` | string | Alternative to recipients — notify all topic subscribers | | `data` | object | Variables for your notification templates | | `actor` | object | The user performing the action (optional) | Use either `recipients` or `topic`, not both. ## Managing Users ### Upsert User ```typescript await client.users.upsert('user_123', { email: 'jane@example.com', first_name: 'Jane', last_name: 'Doe', phone: '+1234567890', avatar: 'https://example.com/avatar.jpg', locale: 'en', timezone: 'America/New_York', custom_fields: { plan: 'premium', company: 'Acme Inc', }, }); ``` ### Get User ```typescript const user = await client.users.retrieve('user_123'); ``` ### List Users ```typescript const usersPage = await client.users.list({ limit: 20 }); ``` ### Delete User ```typescript await client.users.delete('user_123'); ``` ## User Preferences ```typescript // Get preferences const prefs = await client.users.preferences.retrieve('user_123'); // Update preferences await client.users.preferences.update('user_123', { channel_types: { email: true, sms: false }, }); ``` ## Topic Subscriptions ```typescript // Subscribe user to topic await client.users.subscriptions.subscribe('user_123', { topic_name: 'product-updates', }); // List user subscriptions const subs = await client.users.subscriptions.list('user_123'); // Unsubscribe await client.users.subscriptions.unsubscribe('product-updates', { user_external_id: 'user_123', }); ``` ## Topics ```typescript // List topics const topics = await client.topics.list({ limit: 10 }); // Get topic const topic = await client.topics.retrieve('product-updates'); // List topic subscribers const subs = await client.topics.subscriptions.list('product-updates'); // Delete topic await client.topics.delete('product-updates'); ``` ## Notifications & Deliveries ```typescript // List notifications const notifications = await client.notifications.list({ limit: 10 }); // Get notification const notification = await client.notifications.retrieve('notification_id'); // List deliveries for a notification const deliveries = await client.notifications.listDeliveries('notification_id'); // List all deliveries const allDeliveries = await client.deliveries.list({ limit: 10 }); // Get delivery const delivery = await client.deliveries.retrieve('delivery_id'); ``` ## User Notifications & Deliveries ```typescript // List user's notifications await client.users.notifications.list('user_123'); // List user's deliveries await client.users.deliveries.list('user_123'); ``` ## TypeScript The SDK is fully typed: ```typescript import type { User, Notification, Delivery, } from '@notiflows/node'; ``` --- # PHP > Notiflows PHP SDK for server-side integration Source: https://notiflows.com/docs/sdks/server-side/php --- title: PHP description: Notiflows PHP SDK for server-side integration --- The PHP SDK for server-side integration is coming soon. ## Planned Features - Trigger notiflows from your PHP backend - User management and preferences - Topic subscriptions - Notification and delivery tracking --- # Python > Notiflows Python SDK for server-side integration Source: https://notiflows.com/docs/sdks/server-side/python --- title: Python description: Notiflows Python SDK for server-side integration --- The Python SDK (`notiflows`) allows you to trigger notiflows, manage users, and interact with the Notiflows Admin API from your Python backend. ## Installation ```bash pip install notiflows ``` ## Quick Start ```python from notiflows import Notiflows client = Notiflows( api_key="pk_your_api_key", secret="sk_your_secret_key", ) # Trigger a notiflow client.notiflows.run( "welcome-email", data={"name": "Jane"}, recipients=[{"external_id": "user_123"}], ) ``` ## Configuration ```python client = Notiflows( api_key="pk_your_api_key", secret="sk_your_secret_key", ) ``` Both `api_key` and `secret` are required. Find them in your project's **Settings > API Keys**. ## Triggering Notiflows ```python # Send to specific recipients client.notiflows.run( "order-shipped", data={"order_id": "order_789", "tracking_url": "https://example.com/track/789"}, recipients=[ {"external_id": "user_123"}, {"external_id": "user_456"}, ], ) # Send to all subscribers of a topic client.notiflows.run( "new-comment", topic="post_123", data={"commenter": "Jane", "comment": "Great post!"}, actor={"external_id": "user_456"}, ) ``` ## Managing Users ### Upsert User ```python user = client.users.upsert( "user_123", email="jane@example.com", first_name="Jane", last_name="Doe", ) ``` ### Get User ```python user = client.users.retrieve("user_123") ``` ### List Users ```python users_page = client.users.list(limit=20) ``` ### Delete User ```python client.users.delete("user_123") ``` ## User Preferences ```python # Get preferences prefs = client.users.preferences.retrieve("user_123") # Update preferences client.users.preferences.update( "user_123", channel_types={"email": True, "sms": False}, ) ``` ## Topic Subscriptions ```python # Subscribe user to topic client.users.subscriptions.subscribe("user_123", topic_name="product-updates") # List user subscriptions subs = client.users.subscriptions.list("user_123") # Unsubscribe client.users.subscriptions.unsubscribe("product-updates", user_external_id="user_123") ``` ## Topics ```python # List topics topics = client.topics.list(limit=10) # Get topic topic = client.topics.retrieve("product-updates") # Delete topic client.topics.delete("product-updates") ``` ## Notifications & Deliveries ```python # List notifications notifications = client.notifications.list(limit=10) # Get notification notification = client.notifications.retrieve("notification_id") # List deliveries for a notification deliveries = client.notifications.list_deliveries("notification_id") # List all deliveries deliveries = client.deliveries.list(limit=10) # Get delivery delivery = client.deliveries.retrieve("delivery_id") ``` ## User Notifications & Deliveries ```python # List user's notifications client.users.notifications.list("user_123") # List user's deliveries client.users.deliveries.list("user_123") ``` --- # Ruby > Notiflows Ruby SDK for server-side integration Source: https://notiflows.com/docs/sdks/server-side/ruby --- title: Ruby description: Notiflows Ruby SDK for server-side integration --- The Ruby SDK (`notiflows`) allows you to trigger notiflows, manage users, and interact with the Notiflows Admin API from your Ruby backend. ## Installation ```bash gem install notiflows ``` Or add to your Gemfile: ```ruby gem "notiflows" ``` ## Quick Start ```ruby require "notiflows" client = Notiflows::Client.new( api_key: "pk_your_api_key", secret: "sk_your_secret_key" ) # Trigger a notiflow client.notiflows.run("welcome-email", { data: { name: "Jane" }, recipients: [{ external_id: "user_123" }] }) ``` ## Configuration ```ruby client = Notiflows::Client.new( api_key: "pk_your_api_key", secret: "sk_your_secret_key" ) ``` Both `api_key` and `secret` are required. Find them in your project's **Settings > API Keys**. ## Triggering Notiflows ```ruby # Send to specific recipients client.notiflows.run("order-shipped", { data: { order_id: "order_789" }, recipients: [ { external_id: "user_123" }, { external_id: "user_456" } ] }) # Send to all subscribers of a topic client.notiflows.run("new-comment", { topic: "post_123", data: { commenter: "Jane" }, actor: { external_id: "user_456" } }) ``` ## Managing Users ```ruby # Upsert user client.users.upsert("user_123", { email: "jane@example.com", first_name: "Jane" }) # Get user user = client.users.retrieve("user_123") # List users users = client.users.list(limit: 20) # Delete user client.users.delete("user_123") ``` ## User Preferences ```ruby # Get preferences prefs = client.users.preferences.retrieve("user_123") # Update preferences client.users.preferences.update("user_123", { channel_types: { "email" => true, "sms" => false } }) ``` ## Topic Subscriptions ```ruby # Subscribe client.users.subscriptions.subscribe("user_123", { topic_name: "product-updates" }) # List subscriptions subs = client.users.subscriptions.list("user_123") # Unsubscribe client.users.subscriptions.unsubscribe("product-updates", { user_external_id: "user_123" }) ``` ## Topics ```ruby # List topics client.topics.list(limit: 10) # Get topic client.topics.retrieve("product-updates") # Delete topic client.topics.delete("product-updates") ``` ## Notifications & Deliveries ```ruby # List notifications client.notifications.list(limit: 10) # Get notification client.notifications.retrieve("notification_id") # List deliveries client.deliveries.list(limit: 10) # Get delivery client.deliveries.retrieve("delivery_id") ```