Core concepts
Organizations, projects, keys, project users, notifications, audiences, segments, read states and counts.
Organization
Your company or team. It owns team members, projects and the Stripe subscription. Plan limits such as monthly API requests (the requests your app's SDKs make) are counted across all projects of the organization. See Billing and limits.
Project
One app in one environment, for example "Acme web (production)". Everything else (keys, project users, notifications, segments, theme) belongs to exactly one project. A project has:
- a default locale (
enunless you change it), used when no better translation exists; - a theme sent to the SDKs;
- signing keys for user tokens, and allowed origins.
Keys
| Key | Prefix | Used by | Secret? |
|---|---|---|---|
| Publishable key | pk_ | SDKs in browsers and apps, sent with every client request | No. It identifies the project; it can be rotated. |
| Signing keys | sig_ (key id) | Your backend, to sign a user token for each user | The private key is. Only its public half is stored; the private key is shown once. Up to two, for rotation. |
| Secret keys | sk_ | Your backend, as Authorization: Bearer sk_… on server endpoints | Yes. Stored hashed, shown once, revocable. You can have several. |
Every SDK request carries the publishable key and a user token: a short-lived JWT your backend signs with a signing key, naming the user (see Identity verification). See Security for what is safe where.
Project user
An end user of your app, as one project knows them. A project user is identified by
your own user id (userId, up to 255 characters), so the same user id in two projects
is two project users. A project user is created on the SDK's first request for that user,
when you upsert it from your backend (PUT /v1/project-users/{userId}), or as an empty
placeholder when you send a notification to its user id.
Project users have profile fields (email, name, avatarUrl, locale, timezone),
custom attributes (up to 100 key/value pairs) and tags (up to 100 short labels).
Profile fields and attributes can also be synced by the SDK; tags can only
be set from your backend. Attributes and tags power segments and
templates.
Notification
What you send. A notification has:
contents: the title, optional body and optional action label, keyed by locale ({ "en": {…}, "de": {…} }; more than one locale needs the Growth plan or higher);- an
audience: who receives it; - optional
category,actionUrl,imageUrland arbitrarydata; - optional
publishAt(scheduling needs the Growth plan or higher) andexpiresAt; - a status:
draft,scheduled,published,expiredorcanceled.
Published notifications are immutable. Drafts and scheduled notifications can be edited; any notification can be canceled (removed from every feed, kept in history) or deleted.
Audience
| Type | Reaches | Plans |
|---|---|---|
all | Every user of the project, including users created later | All |
users | Up to 1,000 of your user ids | All |
tags | Project users with any (or all) of up to 20 tags | Growth and up |
segment | Project users matching a saved segment | Growth and up |
all, tags and segment are evaluated when each user's feed loads, against the user's
current profile. See Targeting.
Segment
A saved, named condition tree over project user fields, attributes and tags, such as
attributes.plan = "pro" AND tags contains "beta". Because segments are evaluated at read
time, editing a segment changes who sees every notification that targets it, including
ones already published. See Segments.
Feed
The list of notifications a project user can currently see: published, not expired, not
canceled, inside the plan's retention window, and matching the audience. Feed items are
already localized (the locale field tells you which translation was used) and
personalized. They are sorted newest first and paginated with a cursor.
States: read, seen, archived, clicked
Each feed item carries per-user state:
| State | Set when | Effect |
|---|---|---|
seenAt | The user opens the drawer (seen_all), or reads, clicks or archives the item | Stops counting towards the badge |
readAt | The user reads, clicks or archives an item, or uses "mark all as read" (read_all) | Removes the unread dot |
archivedAt | The user archives an item | Hides it from the default feed; visible with status=archived |
| clicked | The user clicks the item or its action | Counted in notification stats |
Users can also mark items unread and unarchive them. "Mark all as read/seen" is one request that covers every notification published before it, however many the user has. It doesn't add to each notification's read stats; only actions on single notifications do.
Counts
Every feed response and action response includes two counts:
unseenCount: unread notifications published since the user last opened the drawer. Show it on the bell badge. It drops to zero when the SDK sendsseen_all.unreadCount: notifications not yet read, excluding archived ones. Show it in the drawer or inbox (for example on an "Unread" tab).
Both counts cover the user's newest 100 notifications, so they never go above 100. The SDK badges show "99+" above 99.