Thomas Schiavone
January 22, 2026

Table of contents
Who this guide is for
The mental model (and how to map your platform)
Tenant hierarchy: modeling real org structure (and why you’ll care)
What data belongs on a tenant (and why you’d actually store it)
The one rule that prevents tenant leaks: always send with tenant context
Targeting groups inside a tenant (roles, teams, segments)
Preferences: stop treating them as global
Tenant-scoped inbox (optional, but huge if you have in-app)
Common mistakes (and how to avoid them)
Key takeaways
Next steps
Most B2B SaaS teams don’t plan to build multi-tenant notification infrastructure. They get there when customers start asking for “our branding,” “our routing rules,” “our Slack workspace,” and “our default preferences.”
The challenge isn’t sending more notifications—it’s sending notifications with the right customer context, every time, without threading a bunch of per-customer logic through your codebase.
Courier Tenants lets you set customer-level defaults once (branding, preferences, metadata, credentials) and have Courier apply them automatically whenever you send with tenant context.
This is for developers building multi-tenant B2B platforms—where your customers are businesses, and you send notifications on their behalf to their users (employees, admins, managers).
If you’ve ever heard:
The easiest way to think about Courier Tenants is: you’re not just sending notifications to users. You’re sending notifications to users in a specific customer context.
In a multi-tenant platform, the same user can belong to multiple orgs. Their identity isn’t just “User 123.” It’s:
Those contexts have different expectations:
tenant_acme)The important part: you only get the value of Tenants when you send with tenant context consistently. See: Sending with tenants.
Real customers aren’t flat. They have departments, teams, projects, and environments—and notifications often need to respect that structure.
Courier Tenants support parent → child relationships (up to four layers) so you can model org structure directly. (Tenants overview, Tenant deep dive)
Copied!
tenant_acme (Company)├─ tenant_acme_hr (Department)│ ├─ tenant_acme_hr_recruiting (Team)│ └─ tenant_acme_hr_peopleops (Team)└─ tenant_acme_eng (Department)├─ tenant_acme_eng_platform (Team)└─ tenant_acme_eng_product (Team)
Hierarchy is less about “pretty structure” and more about inheritance:
It also keeps your model sane when customers ask for “department-level” behavior without you duplicating configuration everywhere.
A tenant shouldn’t just be “an ID.” It’s the customer context you want Courier to apply automatically whenever you send notifications on that customer’s behalf.
brand_id: make it look like them by defaultAttach a tenant’s brand once so you don’t have to pass branding choices on every send.
Value:
default_preferences: customer defaults, before users customizeSet tenant-level defaults so new users start with sensible behavior for that customer.
Value:
properties: template-friendly tenant metadataArbitrary tenant metadata that templates can reference at render time.
Value:
Common examples:
companyName, companySlugtimezone, localesupportEmail, helpCenterUrluser_profile: tenant-scoped recipient defaultsThink “defaults you want merged into the recipient profile when sending within this tenant context.”
Value:
In real white-label setups, different customers often want different delivery plumbing. Tenants can store provider credentials (like a Slack workspace token, a Microsoft Teams webhook URL, or custom SMTP credentials) so Courier can load them automatically when you send with that tenant context.
That means you don’t have to thread “which Slack workspace is this?” through every send call—you attach it to the tenant once.
parent_tenant_id: org structure + inheritanceUse this to build hierarchies and inherit shared defaults.
Value:
When a user can belong to multiple customers, a notification isn’t just “to user X.” It’s “to user X as a member of tenant Y.” Tenant context is what keeps branding, preferences, and inbox visibility scoped correctly.
See: Sending with tenants.
Copied!
import Courier from "@trycourier/courier";const courier = new Courier({apiKey: process.env.COURIER_API_KEY,});await courier.send({message: {to: {user_id: "customer_123",context: {tenant_id: "tenant_acme",},},content: {title: "Invoice available",body: "Your invoice from Acme Corp is ready.",},},});
A practical way to make this reliable in your codebase: treat tenantId as a required parameter anywhere you send notifications. If you don’t have a tenant context, you shouldn’t be sending a tenant-scoped notification.
Tenants define the customer boundary. But “groups” inside that boundary (roles, teams, segments) can live in a few places—what matters is where your source of truth is and whether you want Courier to handle fan-out.
Here are three common approaches:
Option 1: Your own DB
user_ids with to.context.tenant_id.Option 2: Courier Lists
acme.admins, acme.beta-testers) and send to the list.Option 3: Sub-tenant
Rule of thumb: if it’s authorization logic → keep it in your DB (and optionally sync to Lists). If it’s organizational structure → use sub-tenants. If it’s a segment you want Courier to fan out to → use Lists.
For more detail on higher-level targeting patterns (tenant members, nested hierarchies), see: Sending with tenants.
Most apps start with “query users in my DB, then send,” but Courier also supports tenant-level fan-out patterns that are perfect for org-wide announcements.
If you want to notify everyone in a tenant, you can send directly to the tenant:
Copied!
{"to": {"tenant_id": "tenantA"}}
Courier will fan out the send to all users who have a membership in that tenant—while applying tenant-scoped defaults (branding, preferences, metadata, and provider credentials).
include_childrenIf tenant_acme has child tenants (departments/teams), you can target everyone under the company with:
Copied!
{"to": {"tenant_id": "tenant_acme","include_children": true}}
This is the “company-wide announcement” button.
include_parentSometimes you’re sending from a sub-tenant but want to include parent members too (like a department notifying the whole company):
Copied!
{"to": {"tenant_id": "tenant_acme_hr","include_parent": true}}
In multi-tenant platforms, preferences are contextual. Users don’t have one preference profile—they have preferences per tenant context.
Courier supports tenant-scoped defaults users can override:
A pragmatic rollout:
If you’re using hierarchy, this becomes even more useful: a company-level default can apply across the org, while departments/teams override where needed.
If you provide an in-app notification feed, tenant context keeps inbox experiences from collapsing into one blended stream.
Start here: Inbox with tenants.
This pairs naturally with an org switcher:
One easy gotcha: messages sent with tenant context only show up when your Inbox SDK is initialized with the same tenantId. And if you’ve created tenant memberships (and especially if auto-infer is enabled), you still need to set tenantId when you initialize the SDK—otherwise messages can “disappear” because the SDK is looking at a different tenant inbox.
You’re probably not sending with tenant context consistently. Start here: Sending with tenants.
You probably modeled preferences globally. Make them tenant-aware: User tenant preferences.
Branding is being handled ad-hoc (per send, per template, per channel). Move the defaults into tenant configuration: Tenants overview.
Use tenant-scoped inbox rendering and tenant-scoped sends: Inbox with tenants.
tenant_id on every send to prevent branding mismatches, preference leaks, and inbox blendingA solid order of operations for implementation:
And if you want an example of a platform scaling notifications without turning it into a maintenance sink, Side’s story is a good read: How Side unified notifications.

Is texting patients a HIPAA violation?
The short answer: texting patients is fine until an unsecured text carries protected health information. This post draws the line with side-by-side SMS examples, covers the minimum-necessary rule and when a BAA is required, and shows the template pattern that makes it structurally impossible to leak PHI into a text.
By Emily Lane
June 21, 2026

watchOS 27 Notifications: What Changed and How to Adapt Your Product Sends
Apple's watchOS 27, announced at WWDC 2026, presents Apple Watch notifications based on relevance instead of arrival time and expands contextual Smart Stack widgets. Because watch notifications mirror iPhone push, your push strategy is your watch strategy. This guide covers what product and B2B notification teams should change: setting APNs interruption levels honestly, writing glanceable payloads, routing by urgency across push, email, SMS, and in-app inbox, using widgets for status content, and handling the split audience after watchOS 27 drops Series 8, Ultra 1, and SE 2.
By Kyle Seyler
June 09, 2026

Your Entire Lifecycle Marketing Department, Run from Claude Fable 5
With the rollout of Claude' Fable model, one thing is becoming increasingly clear. Marketing execution (especially the long-tail work), will be done in an AI editor. In Courier, connect your agent to the MCP server or CLI, install Courier Skills, and keep a small folder of markdown context files. From there, one person with a coding agent covers the work that used to require a lifecycle marketer, an email designer, a marketing ops hire, and an engineer: building journeys, shipping templates, auditing every notification, and debugging delivery without opening a dashboard.
By Kyle Seyler
June 09, 2026
© 2026 Courier. All rights reserved.