Skip to main content
In a B2B product, each customer is a company with its own set of users. In Keystone those users are the customer’s employees, working across its projects, departments and teams. Each customer controls its own notifications — across account, per project, and per user:
  • Which notifications exist, and whether each is on, off, or mandatory.
  • Who receives them, by role.
  • Which channels they go out on, and how often.
This guide models that in SuprSend using Keystone, a project-management product, and its two customers — Acme and Globex. It walks through Acme; Globex is set up the same way.
Example app. Keystone’s source code is coming soon.

Keystone architecture overview

Keystone is a project-management product. It serves several customers — Acme and Globex, for example — each running multiple projects, and keeps each customer’s notifications entirely separate. What Keystone notifies users about Who can change what
  • An Admin sets the defaults across the account: which notifications exist, whether each is on, off or mandatory, who receives it by role, the digest cadence, conditions and channels. An admin also sets the customer’s branding and edits the message content.
  • A Project Manager decides who is on their project and in which role. They set the project’s default notification settings, and can adjust individual members’ preferences across the project — as can an admin.
  • A Project Sponsor, Process Owner or Team Member controls only their own notifications: a default that applies across all their projects, and an override for any single project.
Keystone roles and what each can change
The sections below map each of these concepts onto SuprSend, using Acme.

Keystone concept mapping to SuprSend

Here’s how the Keystone concepts map to SuprSend:
Keystone concepts mapped to SuprSend entities

Data modelling in SuprSend

1. Workspace

Each customer gets its own workspace. All of Acme’s assets and projects live in Acme’s workspace; Globex has its own and shares nothing with it.
One workspace per customer, fully isolated
A workspace holds its own copy of everything, shared with no other by default:
  • Notification categories, templates, and workflows
  • Vendors, users, and preferences
  • API keys and logs
Differences between a customer’s projects, such as their preferences, are handled inside the workspace by tenants. Environments. A workspace is also SuprSend’s environment boundary. Most products run one workspace per environment:
  • dev — build and experiment.
  • staging — test safely, with Test Mode holding back delivery to real users.
  • prod — live sends.
One workspace per environment: dev, staging, prod
When each customer has its own staging and production, you can create multiple workspaces per customer: acme-staging and acme-prod, globex-staging and globex-prod.
To keep more than one workspace in sync — for example Staging and Production — replicate categories, templates, and workflows with the SuprSend CLI.
A customer doesn’t always need to be modelled as a workspace. You can model each customer as a tenant, with its projects as sub-tenants. See tenants vs workspaces to choose.

2. Projects as tenants

Each of Acme’s projects is a tenant. acme-aero and acme-bms are two tenants inside Acme’s workspace. A project’s tenant_id is the project’s id. A tenant is a segment inside a workspace — an organization, a team, or a project. SuprSend applies its configuration when the trigger passes its tenant_id. A project’s tenant holds its preferences (see Customizing preferences) and, if it needs one, its own template variant (see Templates). Keystone creates a tenant for each project through the Create/update tenants API:
Every workspace also has a default tenant — here, Acme’s own branding and settings. SuprSend applies it whenever a notification is triggered without a tenant_id, and any project tenant falls back to it for anything the project doesn’t set itself. Keystone’s projects set no branding of their own, so every message carries Acme’s.
Projects modelled as tenants inside a customer's workspace
Recipients come from the project’s object, covered next.
A tenant can also carry per-project user properties — different channel identities or properties for the same user, per project (user-tenant mapping). Keystone doesn’t use it; a user’s role lives on the project’s object.

3. Projects as objects and their subscriptions

Each project is also an object — the record of who is on it. Users subscribe to the object, and a workflow triggered on it fans out to every subscriber, so a notification reaches the whole project without listing anyone in the trigger. Keystone creates the object with the Create/update objects API, then adds each user with the subscription API. Their role is stored as a subscription property:
Project members and their roles in the Acme project

Projects and roles in Keystone's Acme project

The role sits on the subscription in the project, and not on the user, so the same user can hold a different role on each project. A workflow reads it as $recipient.subscription.role. A trigger names the object as the recipient and passes the project’s tenant_id:
Object subscribers in SuprSend

Objects in SuprSend

When a notification should reach only some roles, the workflow filters the fan-out by role — see Workflows and conditions.
An object can subscribe to another object, so you can model nested structures — a department containing several teams, say — where a trigger on the parent fans out across levels, up to two by default. Keystone’s projects are flat, so it isn’t used.

4. Notification categories

Every Keystone notification belongs to a notification category — the tree that holds a customer’s notification settings. It has three levels, and only the bottom one carries preferences:
  • RootSystem, Transactional, or Promotional. Fixed; Keystone puts every notification under Transactional.
  • Section — an optional grouping for the preference page. Carries no settings.
  • Sub-category — one per notification. This is what a user opts in and out of, and where the customer’s defaults live. Each workflow is assigned one sub-category’s slug, and several workflows can share the same sub-category.
For Keystone’s customers, the sections are Assignments, Project status, Tasks, Collaboration and Budget:
Notification category tree with sections and sub-categories
Each sub-category holds the defaults across the account for its notification:
  • Default preferenceOn, Off, or Can’t Unsubscribe (must-send). Channel-level opt-in/out still applies unless a channel is marked mandatory.
  • Default channels — email, in-app inbox and Slack.
  • Digest schedule — an optional cadence a user can pick, such as Instant, Daily, or Weekly, that drives a Digest node at send time.
  • Condition properties — values the workflow reads at send time as $category.properties.<key>. Keystone uses them for recipient rules — which roles receive the notification (role), plus specific users to always notify (include) or exclude (except), each a multi-select list of the workspace’s users — and for thresholds such as a cost limit.
Exposing categories in your app. Keystone doesn’t set these defaults on its customers’ behalf — it exposes the same category tree on two screens:
  • Customer-wide, for an admin — changes are stored on the category itself, as its default preference.
  • Per project, for a project manager or an admin — changes are stored against that project’s tenant, so one project’s changes never affect another’s.
A user’s own choices are stored against the user. See Customizing preferences for how the three resolve. What an admin can lock. A category set to Can’t Unsubscribe can’t be switched off by a project or by a user — only its non-mandatory channels stay adjustable. An admin can also hide a category for a project entirely, in which case it disappears from those users’ preference page and stops sending, whatever they had chosen before.
Sub-category default settings, channels, and condition properties

5. Customizing preferences

A preference decides whether someone receives a notification for a category, and on which channels. Three layers set it: a user’s own choice wins over the project’s default, which wins over the customer-wide default.
Each layer is set in a different place: Every layer sets the same four controls:
  • Whether it’s sent — On, Off, or Can’t Unsubscribe.
  • Channels — which ones the notification goes out on.
  • Digest cadence — how often batched notifications arrive.
  • Condition propertiesrole, include, except, and thresholds like cost.
Exposing preferences in your app. Keystone builds its own UI surfaces on these APIs:
  • A user’s Notifications tab, for their own preferences.
  • Admin screens for the customer-wide defaults, a project’s defaults, and any user’s preferences on a project.
  • If you’d rather not build the end-user preference centre, SuprSend ships prebuilt and headless preference components.
Preference screens for a user, a project, and the whole customer

6. Workflows and conditions

A workflow is what runs when a notification fires — its steps, channels, and conditions. Each is linked to one notification category and to a template per channel. Triggering. Keystone triggers a workflow two ways, each passing the project’s tenant_id:
  • Direct API trigger — Keystone names the workflow, its recipients, and the data. The recipient is the project object, so SuprSend fans the notification out to everyone on that project (see Projects as objects) — the backend never has to look up or list members. It can also name a single user instead, for something aimed at one person like a task assignment.
  • Event-based trigger — Keystone emits what happened, and every live workflow linked to that event fires, with SuprSend resolving the recipient. One product action can drive several notifications, and adding another one later is a dashboard change, not a backend change.
Compare the two in Event vs Workflow API. One workflow can serve every project. The tenant_id in the trigger decides which project’s settings apply. Preferences and recipient matching. SuprSend first drops anyone opted out of the workflow’s category. The trigger step then filters what’s left against the category’s recipient rules:
A user is kept if their role is in role and they aren’t in except, or if they’re in include. That’s how one workflow reaches only the project manager for a completed task, but the whole team for a started project. Steps used. Beyond a plain send, Keystone’s workflows use two nodes:
  • Wait Until — in Tasks Coming Up, the reminder waits for whichever comes first: if the task is closed, nothing sends; if the due date comes within three days, the reminder goes out.
  • Digest — in Comment Added, comments collect into one summary instead of a message per comment. Its schedule type is Preference Category, so it uses the cadence the recipient picked, falling back to the project default, then the category default, in the recipient’s timezone.
Keystone builds and publishes workflows through the Workflows Management API, or in the SuprSend dashboard’s visual builder.
A Keystone workflow in the SuprSend workflow builder

7. Templates

A template holds a notification’s content for each channel — email, in-app inbox, and any others. Content is Handlebars, with variables from the trigger, the recipient, and the tenant. The {{$brand.*}} variables are filled from the tenant at send time. See the full variable list. Keystone creates and publishes templates through the Templates Management API — create the template, set each channel’s content, then commit a version. Exposing templates in your app. Admins edit a notification’s content inside Keystone with the embedded template editor, authenticated with a scoped token. There’s one template per notification, so an edit applies to every project.
The embedded template editor inside Keystone
Conditional content. A block can be shown only to recipients who match a condition — a display condition. One notification often reaches several roles at once, so Keystone’s Project Assigned email gates its paragraphs on $recipient.subscription.role, and each role reads the wording meant for it from one template.
Keystone uses one default template per notification. If a project needs different content, add a variant scoped to its tenant.

8. Vendors

A vendor is the delivery provider that sends — email, SMS, push, or chat. Vendors are configured once per workspace, so every project sends through the customer’s own providers, under one sender identity. Keystone’s templates are authored for email, in-app inbox and Slack. Adding a vendor for another channel makes that channel available to the same templates.
Vendors configured once per workspace
A project can send from its own identity — a different email domain or SMS sender — with a tenant vendor. Keystone doesn’t use this.

End-to-end notification flow

With the categories, templates, workflows and vendors in place, one trigger runs the whole thing. Take a completed task on the Aerospace project: it should reach only that project’s manager. Keystone triggers the workflow with the project object as the recipient and the project as tenant_id:
SuprSend resolves it end to end: One trigger, two entities: the tenant supplied the preferences, the object supplied the recipients.

What a project keeps to itself, and what it shares

Everything for a customer lives in its own workspace. Within it:

Other common scenarios

The same model extends beyond projects:
  • Units other than projects. Departments, teams, or regions map the same way — each a tenant and an object.
  • One user across many projects. A user exists once in the workspace and subscribes to several objects, with a different role and different preferences on each.
When to use a different model.

Reference: credentials and APIs

Keystone talks to SuprSend across four surfaces. Secrets stay server-side; the two browser SDKs only ever get a short-lived JWT minted by the backend.

Credentials

The frontend embeds authenticate the end user with a signed user token — an ES256 JWT the backend mints with the workspace’s signing key, carrying the user’s distinct_id. Keystone uses it for the embedded preference centre and template editor. Every key is stored in environment variables, never in client code.

Node SDK and Hub REST — per-workspace credentials

On hub.suprsend.com, using the workspace key and secret (backend SDK) or API key (REST). Keystone uses the Node SDK.

Management API — account Service Token

On management-api.suprsend.com, with the workspace as a path segment.