Send in-app notifications from Node.js
No queue to run. Send a notification to one user id or to everyone from your server or an agent, and show it in a bell that one script tag adds to your app, or read it through the API and draw your own.
Keys
Sending uses a secret key on your server, which also signs each user id with the notifications signing secret. The browser reads with a publishable key and that signature. Create both under Dashboard → API keys.
Publishable key pasted into the page that embeds the form
NORDVA_SECRET_KEY=nv_live_… 01Server: Node.js
notify.ts
export async function notifyUser(input: Record<string, unknown>) {
const res = await fetch("https://api.nordva.dev/v1/notifications", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.NORDVA_SECRET_KEY!}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
user_id: input.user_id,
title: "Export ready", // up to 200 characters
body: "Your CSV is ready to download.", // up to 2,000 characters
action_url: "https://app.example.com/exports",
icon: "check", // check | warning | info | error
}),
});
if (!res.ok) {
const { error } = await res.json();
throw new Error(`${error.code}: ${error.message}`);
}
const { data } = await res.json();
// data.id: send an Idempotency-Key header to make retries safe
return data;
} Every plan, per project per month: Free 1,000, Indie 20,000, Builder 200,000. Past that the API answers PLAN_LIMIT_REACHED. To post to everyone, send audience: "all" instead of user_id.
02Server: sign the user id (signing secret, never in the browser)
import { createHmac } from "node:crypto";
// Give the page the signed-in user's id and this hash, for example in the HTML you render.
export function userHash(userId: string): string {
return createHmac("sha256", process.env.NORDVA_NOTIFICATIONS_SECRET!).update(userId).digest("hex");
} Create the signing secret once, with POST /v1/notifications/signing-secret and a secret key or through the MCP server. It is shown once: store it as NORDVA_NOTIFICATIONS_SECRET on your server.
03Verify from a terminal
curl -s "https://api.nordva.dev/v1/notifications/unread?user_id=user_123" \
-H "Authorization: Bearer nv_live_…" | jq '.data.notifications | length' Behaviour worth knowing
- Every response is
{ data, error, meta }. On failureerror.codeis a stable string such as VALIDATION_ERROR, PLAN_LIMIT_REACHED or RATE_LIMITED, with a remediation message. - POST and PATCH requests accept an
Idempotency-Keyheader; the same key with the same body returns the original response for 24 hours. - Rate limits per key: 30 requests a minute on Free, 120 on Indie, 500 on Builder. A 429 carries Retry-After.
- Browser calls with a publishable key must come from an origin registered on the project, otherwise the API answers ORIGIN_NOT_ALLOWED.