Security

Which keys are safe where, user tokens, allowed origins, server-only tags and what users can change.

Keys and where they belong

CredentialBrowser / appBackendNotes
Publishable key pk_…YesYesIdentifies the project. Useless without a user token. Rotating it (on the API keys page) breaks every deployed app that still uses the old key.
Signing key (private key PEM)NeverYesSigns user tokens. Anyone holding it can impersonate any user of the project. NotificationStream only stores its public half.
Secret key sk_…NeverYesFull server API access for one project: send, edit, delete, read project users.
User token (JWT)YesSigned hereValid for one user of one project until its exp (at most 7 days).

Secret keys:

  • are stored hashed and shown only once, when you create them;
  • can be named and revoked individually; a revoked key stops working everywhere within about 30 seconds;
  • show when they were last used (updated at most hourly).

Use one secret key per service or environment so you can revoke them independently.

Signing keys:

  • are generated in your browser; only the public key is uploaded, and the private key is shown once;
  • can be deleted individually; tokens signed with a deleted key stop working within about a minute;
  • show when they were last used (updated at most hourly). A project can have two, so you can rotate without downtime.

Identity verification

Every client request must carry a valid user token, so nobody can read another user's notifications by guessing their user id. It can't be turned off. Keep tokens short-lived: a leaked token works until it expires. See Identity verification.

Allowed origins

In the project settings you can list up to 50 origins (such as https://app.example.com) that may call the client endpoints from a browser. Each entry is matched exactly (scheme, host and port, no wildcards). When the list is set:

  • every client request from a browser whose Origin header is not on the list fails with 403 forbidden (realtime connections are closed with code 4403);
  • requests without an Origin header, such as native apps and servers, are not affected.

Allowed origins stop other websites from using your publishable key, or a user token that leaked to them, from a browser. They are not a substitute for user tokens: non-browser clients can send any origin they like.

What users can change

Everything the SDK sends is controlled by the user, who can modify the app or call the API directly. With a valid user token a user can:

  • set their own profile fields (email, name, avatarUrl, locale, timezone) and attributes (POST /v1/client/profile);
  • read their own feed and mark their own notifications read, unread, archived, unarchived or clicked. Actions only apply to notifications that are currently in their feed.

A user cannot:

  • set or change tags: tags are server-only, so tag audiences can be trusted;
  • see notifications addressed to other users, or other users' state;
  • send notifications, or read anything through the server endpoints.

Caution

Because attributes can be set from the client, a user could change their own attributes.plan to match a segment. Target confidential notifications with tags or a users audience, not with attribute-based segments. Your backend can still set attributes (PUT /v1/project-users/{userId}) for personalization and non-sensitive targeting.

Content safety

  • Notification text is rendered as text. Bodies support only bold, italic and links, and the SDK UIs drop any link that isn't http(s), mailto or relative. No HTML is ever rendered.
  • Template values are inserted as plain text, and Markdown-escaped in bodies, so user data can't inject links or formatting.
  • data, actionUrl and imageUrl are delivered to every recipient as-is. Don't put secrets in them.

Data protection

  • Delete a project user and all their notification state with DELETE /v1/project-users/{userId} (for example for GDPR erasure requests).
  • Feeds only show notifications within your plan's retention window, and notifications are deleted after 365 days, or after your custom plan's window if that is longer.
  • Only send the profile fields you need: every profile field is optional.