Quickstart
Register, create a project, add the bell to your app and send your first notification.
This guide takes you from a new account to a notification showing up in your app. You need a backend that can make HTTPS requests and a web or React Native app with signed-in users.
Register and start your trial
Register at notificationstream.com. Right after,
the dashboard asks you to pick a plan (preselected if you chose one on the pricing page)
and complete the Stripe checkout. Every plan starts with a 3-day free trial. Until your
organization has an active or trialing subscription, the dashboard stays on that step and
the REST API rejects writes (such as sending a notification) with 402 payment_required.
See Billing and limits.
Create a project
In the dashboard, create a project for each app and environment, for example "Acme (production)" and "Acme (staging)". Projects never share project users or notifications. Starter includes one project, Growth five and Scale unlimited. Set the default locale to the language most of your users read.
Save your keys
Creating a project also creates its first keys:
| Key | Looks like | Where it goes |
|---|---|---|
| Publishable key | pk_… | Your frontend. Safe to ship in browsers and apps. |
| Signing key | A private key PEM | Your backend only. Signs user tokens. |
| Secret key | sk_… | Your backend only. Sends notifications through the REST API. |
The dashboard shows the backend keys once, as a .env block to copy or download:
NOTIFICATIONSTREAM_SECRET_KEY and NOTIFICATIONSTREAM_SIGNING_KEY (the PEM). The private signing key is generated in your
browser and never sent to NotificationStream. If you lose a key, create a new one on the
project's API keys page. The project page then walks you through the remaining
steps with your publishable key already filled in.
Sign a user token on your backend
The SDK must prove which user it is acting for with a user token: a short-lived ES256
JWT whose sub is the user id, signed with your signing key. Add an endpoint that returns
one for the signed-in user:
import { createUserToken } from "@notificationstream/node";
import { getSession } from "@/lib/auth";
export async function GET() {
const session = await getSession();
if (!session) return new Response("Unauthorized", { status: 401 });
const token = await createUserToken(process.env.NOTIFICATIONSTREAM_SIGNING_KEY!, session.user.id);
return new Response(token, { headers: { "Cache-Control": "no-store" } });
}Tokens last an hour by default. Other languages are in Identity verification.
Install an SDK and render the bell
Pass the publishable key, the signed-in user (with the same id you signed) and the user token from the previous step. A function lets the SDK fetch a new token when the old one expires.
pnpm add @notificationstream/jsimport { createNotificationClient } from "@notificationstream/js";
const client = createNotificationClient({
publishableKey: "pk_…",
user: { id: user.id, name: user.name },
token: () => fetch("/api/notifications/token").then((r) => r.text()),
});
// Render your own bell and inbox from the state.
client.subscribe((state) => {
renderBadge(state.unseenCount);
renderList(state.items);
});The JavaScript SDK has no bundled UI; its headless UI core gives your components the same behaviour as the React ones.
The SDK loads the feed and applies your dashboard theme. It refetches when users come
back to your app. To update an open screen, call useRealtime() in a component under the
provider (or client.realtime(); Growth plan and up), and usePolling(60_000) (or
client.poll(60_000)) to also check on a timer while realtime isn't connected. See
Polling, realtime and performance and the
SDK pages for every option.
Send your first notification
From your backend, with the secret key:
curl https://api.notificationstream.com/v1/notifications \
-H "Authorization: Bearer $NOTIFICATIONSTREAM_SECRET_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: welcome-user_123" \
-d '{
"audience": { "type": "users", "userIds": ["user_123"] },
"contents": {
"en": {
"title": "Welcome aboard!",
"body": "Your organization is ready. Invite your team to get started.",
"actionLabel": "Invite people"
}
},
"category": "onboarding",
"actionUrl": "/settings/team"
}'The response is 201 Created with the stored notification ("status": "published").
On the Growth plan and up you can greet users by name with a
template such as {{user.name | there}}.
With the Node SDK the same call is
await ns.notifications.create({ audience, contents, category, actionUrl }).
Watch it arrive
The bell picks it up on its next fetch: right away with realtime, otherwise when you
reload the page, switch back to its tab, or after one poll interval if you poll.
Without realtime, notifications sent to all, tags or segments can take up to about
30 seconds longer, because the broadcast list is cached per project. To check from the
server side, read the user's feed:
curl "https://api.notificationstream.com/v1/project-users/user_123/feed" \
-H "Authorization: Bearer $NOTIFICATIONSTREAM_SECRET_KEY"User ids
The user id is your own stable id for the user (a database id, not an email that can change). You can target users by id before they ever open your app: they are created as placeholder project users and see the notification on their first visit.
Next steps
- Target groups of users with tags and segments.
- Ship content in several languages with localization.
- Match the bell to your brand with theming.
- Lock the project down before launch: Security.