@dnotifier-realtime/dnotifier
v1.1.20
Published
Real-time communication & AI SDK
Maintainers
Readme
DNotifier JavaScript SDK
DNotifier is a real-time notification and messaging SDK for Node.js and browsers. It supports WebSocket and HTTP transports, binary streaming, framed packets, an AI pipeline, knowledge-base (RAG) APIs, optional session logging, and multi-step workflows with agents and observability.
Features
- WebSocket and HTTP transport
- Secure authentication handshake
- Real-time messaging (text and binary)
- Large payloads with automatic chunking and reassembly
- AI:
sendAI,fetchAIHistory, semanticsearch - Knowledge base: add, update, get, list, delete, and search documents
- Chat history fetch and delete
- Optional SDK session logging (
logs: true) - Workflows: named agents, sequential pipelines, dashboard observability
- Plan limits from auth (
getPlanLimits)
Installation
npm install @dnotifier-realtime/dnotifierQuick start
import { DNotifier } from "@dnotifier-realtime/dnotifier";
const notifier = new DNotifier({
appId: "your-app-id",
secret: "your-app-secret",
transport: "ws", // or "http"
userId: "user-123",
onConnected: () => console.log("Connected"),
onMessage: (msg) => {
console.log("From:", msg.metadata.sender);
console.log("Payload:", msg.payload.toJSON());
},
onDisconnected: () => console.log("Disconnected"),
});
await notifier.connect();
notifier.send({
senderId: "user-123",
receiverId: "user-456",
data: { type: "text", text: "Hello from DNotifier" },
});For Node.js WebSocket, pass a WebSocket implementation:
import WebSocket from "ws";
const notifier = new DNotifier({
appId: "your-app-id",
secret: "your-app-secret",
transport: "ws",
userId: "user-123",
WebSocketImpl: WebSocket,
onConnected: () => {},
onMessage: () => {},
onDisconnected: () => {},
});Connection and configuration
| Option | Type | Description |
|--------|------|-------------|
| appId | string | Your DNotifier application id |
| secret | string | Application secret |
| transport | "ws" | "http" | "ws" for realtime messaging; "http" for RPC-style AI/RAG calls |
| userId | string | End-user or agent id for auth and routing |
| logs | boolean | When true, tracks AI sessions in the DNotifier logs dashboard |
| url | string | Optional custom WebSocket URL |
| WebSocketImpl | class | Required in Node when using transport: "ws" |
| onConnected | function | Called when the client is ready |
| onMessage | function | Called with { metadata, payload } for incoming messages |
| onDisconnected | function | Called when the connection closes |
await notifier.connect();
const limits = notifier.getPlanLimits();
console.log(limits?.aiEnabled, limits?.maxAIRequestsPerMonth);After connect(), these properties are available:
| Property | Description |
|----------|-------------|
| isConnected | Whether the client is connected |
| aiEnabled | Whether AI is enabled for the current plan |
| authToken | Auth token from the last successful connect |
| messageSizeLimit | Max message size in bytes (from plan) |
Real-time messaging
Text and structured messages
Use any type in data that fits your app (text, image, audio, doc, etc.):
await notifier.send({
senderId: "user-123",
receiverId: "user-456",
data: {
type: "text",
text: "hello from dnotifier sdk!",
},
saveHistory: true, // default true
});Send to multiple receivers:
await notifier.send({
senderId: "user-123",
receiverIds: ["user-456", "user-789"],
data: { type: "text", text: "Hello everyone" },
});Images, audio, and binary
Prefer send() with a typed payload. The SDK chunks large payloads automatically.
import { readFileSync } from "fs";
const imageBytes = readFileSync("./photo.png");
await notifier.send({
senderId: "user-123",
receiverId: "user-456",
data: {
type: "image",
content: imageBytes,
},
});Handle incoming messages in onMessage:
onMessage: (msg) => {
const body = msg.payload.toJSON();
if (body?.type === "image") {
// body.content — image bytes or base64 depending on your app convention
} else if (body?.type === "text") {
console.log(body.text);
}
},Raw binary (sendBinary)
notifier.sendBinary({
senderId: "user-123",
receiverIds: ["user-456"],
buffer: new Uint8Array([0x00, 0x01, 0x02]),
type: "binary",
});AI
Requires aiEnabled on your plan. Use transport: "http" for request/response AI calls.
Simple prompt
const response = await notifier.sendAI({
senderId: "user-123",
message: { text: "Summarize our refund policy in two sentences." },
});Chat-style messages
const response = await notifier.sendAI({
senderId: "user-123",
message: {
useKnowledgeBase: true,
messages: [
{ role: "system", content: "You are a helpful support agent." },
{ role: "user", content: "How do I reset my password?" },
],
},
saveHistory: true,
});Continue an existing session
const first = await notifier.sendAI({
senderId: "user-123",
message: { text: "Start a new support session." },
});
const sessionId = first?.metadata?.packet?.id;
await notifier.sendAI({
senderId: "user-123",
sessionId,
message: { text: "Follow-up question in the same session." },
});AI history
const history = await notifier.fetchAIHistory({ senderId: "user-123" });
await notifier.deleteAIHistoryMessage({
senderId: "user-123",
messageId: "message-id-to-delete",
});Knowledge base (RAG)
// Add
await notifier.addDocument({
senderId: "user-123",
recordId: "doc-001",
content: "Refund requests are processed within 5 business days.",
type: "text",
metadata: { source: "help-center" },
});
// Update
await notifier.updateDocument({
senderId: "user-123",
recordId: "doc-001",
content: "Updated refund policy text...",
type: "text",
});
// Get one document
const doc = await notifier.getDocument({
senderId: "user-123",
recordId: "doc-001",
});
// List
const list = await notifier.listDocuments({
senderId: "user-123",
limit: 20,
offset: 0,
type: "text",
});
// Semantic search
const hits = await notifier.search({
senderId: "user-123",
query: "how long do refunds take",
limit: 5,
minSimilarity: 0.7,
filterbySource: "help-center",
});
// Delete
await notifier.deleteDocument({
senderId: "user-123",
recordId: "doc-001",
});Chat history
await notifier.fetchChatHistory({
senderId: "user-123",
receiverIds: ["user-456"],
});
// Server responds via onMessage with the history payload.
await notifier.deleteChatHistoryMessage({
senderId: "user-123",
messageId: "message-id-to-delete",
});Session logging
Enable automatic AI session tracking in the DNotifier dashboard:
const notifier = new DNotifier({
appId: "your-app-id",
secret: "your-app-secret",
transport: "http",
userId: "user-123",
logs: true,
onConnected: () => {},
onMessage: () => {},
onDisconnected: () => {},
});When logs: true, sendAI reports session start, completion, and token usage to DNotifier.
Workflows and agents
Build deterministic, multi-step AI pipelines with named agents, shared workflow state, and optional observability (execution and step telemetry in the DNotifier workflow dashboard).
1. Define agents
import { DNotifier } from "@dnotifier-realtime/dnotifier";
const intentAgent = DNotifier.defineAgent({
name: "intent-agent",
async run(ctx) {
await ctx.sendAI({
message: { text: "Classify: search or general?" },
saveHistory: false,
label: "Intent classification",
});
return { intent: "search" };
},
});
const generalAgent = DNotifier.defineAgent({
name: "general-agent",
async run(ctx) {
const question = ctx.input?.question ?? ctx.input;
return { answer: `You asked: ${question}` };
},
});2. Define a workflow
const workflow = new DNotifier.Workflow({
name: "intent-router",
description: "Route user input to search or general Q&A",
observability: true,
async entry(ctx) {
const { intent } = await ctx.runAgent("intent-agent");
if (intent === "search") {
await ctx.search({
query: String(ctx.input),
limit: 5,
label: "Knowledge search",
});
return { branch: "search" };
}
const answer = await ctx.runAgent("general-agent", {
input: { question: ctx.input },
});
return { branch: "general", answer };
},
}).registerAgents({
"intent-agent": intentAgent,
"general-agent": generalAgent,
});3. Run the workflow
const notifier = new DNotifier({
appId: "your-app-id",
secret: "your-app-secret",
transport: "http",
userId: "user-123",
onConnected: () => {},
onMessage: () => {},
onDisconnected: () => {},
});
await notifier.connect();
const run = await notifier.runWorkflow({
workflow,
input: "How does messaging work?",
});
console.log(run.executionId); // present when observability: true
console.log(run.result); // { branch: "search" } or similar
console.log(run.state); // shared state from the runMulti-stage pipeline example
const createAgent = DNotifier.defineAgent({
name: "content-creator",
async run(ctx) {
const brief = ctx.input;
const aiResponse = await ctx.sendAI({
message: {
useKnowledgeBase: false,
messages: [
{ role: "system", content: "Write a blog draft in markdown." },
{ role: "user", content: `Topic: ${brief.topic}` },
],
},
saveHistory: false,
label: "Draft creation",
});
const content = aiResponse?.data?.content ?? "";
ctx.state.draft = content;
return { content };
},
});
const workflow = new DNotifier.Workflow({
name: "blog-article-writer",
description: "Create, refine, and optimize a blog post",
observability: true,
async entry(ctx) {
const draft = await ctx.runAgent("content-creator");
const refined = await ctx.runAgent("clarity-editor", {
input: { content: draft.content },
});
return { article: refined.content };
},
}).registerAgents({
"content-creator": createAgent,
"clarity-editor": clarityEditorAgent,
});WorkflowContext (inside entry and agent run)
| Member / method | Description |
|-----------------|-------------|
| ctx.input | Workflow input passed to runWorkflow (or overridden per runAgent) |
| ctx.state | Shared mutable object for the current execution |
| ctx.agentName | Name of the agent currently running (inside agent handlers) |
| ctx.runAgent(name, { input? }) | Invoke a registered agent |
| ctx.sendAI({ message, saveHistory?, label? }) | AI call; steps recorded when observability is on |
| ctx.fetchAIHistory({ label? }) | Fetch AI history |
| ctx.search({ query, limit?, minSimilarity?, filterbySource?, label? }) | Semantic search |
| ctx.addDocument(opts) | Add knowledge-base document |
| ctx.updateDocument(opts) | Update document |
| ctx.deleteDocument(opts) | Delete document |
| ctx.listDocuments(opts?) | List documents |
| ctx.getDocument(opts) | Get document by id |
| ctx.recordStep({ label, input?, output?, status?, type? }) | Record a custom step when observability is enabled |
When observability: true on the workflow, SDK calls and recordStep are sent to the DNotifier workflow dashboard. No extra setup is required beyond connect().
API reference
DNotifier
| Method | Description |
|--------|-------------|
| connect() | Authenticate and connect |
| disconnect() | Close the WebSocket connection |
| getPlanLimits() | Plan limits from last auth |
| send({ senderId, receiverId?, receiverIds?, data, saveHistory? }) | Send a message |
| sendBinary({ senderId, receiverIds, buffer, type? }) | Send raw bytes |
| sendAI({ senderId, message, saveHistory?, sessionId? }) | Send to AI pipeline |
| fetchAIHistory({ senderId }) | Fetch AI conversation history |
| deleteAIHistoryMessage({ senderId, messageId }) | Delete one AI history message |
| fetchChatHistory({ senderId, receiverIds }) | Request chat history (response via onMessage) |
| deleteChatHistoryMessage({ senderId, messageId }) | Delete one chat history message |
| search({ senderId, query, limit?, minSimilarity?, filterbySource? }) | Semantic search over knowledge base |
| addDocument({ senderId, recordId, content, type?, metadata? }) | Add a document |
| updateDocument({ senderId, recordId, content, type?, metadata? }) | Update a document |
| getDocument({ senderId, recordId }) | Get a document |
| listDocuments({ senderId, limit?, offset?, type? }) | List documents |
| deleteDocument({ senderId, recordId }) | Delete a document |
| runWorkflow({ workflow, input, senderId? }) | Run a workflow after connect() |
| sendWithOpenAI(...) | Deprecated — use sendAI |
Workflow exports
| Export | Description |
|--------|-------------|
| DNotifier.defineAgent({ name, run }) | Create a named agent |
| DNotifier.Workflow | Workflow definition class |
| DNotifier.WorkflowContext | Context type passed to entry and agent run |
| DNotifier.WorkflowError | Workflow validation and runtime errors |
| Agent | Agent class (also available via defineAgent) |
runWorkflow is the recommended way to execute a workflow. WorkflowRunner is exported for advanced custom hosting but is not required for typical use.
Message and payload types
DNotifierMessage
metadata—{ id, sender, timestamp, type }payload—Payloadinstance
Payload
toJSON()— parse JSON bodytoString()— string bodytoBase64()— base64 stringraw()— raw bytes
getPlanLimits() return value
messagesHardLimit,maxAIRequestsPerMonth,maxAIWordsPerMonthknowledgeBaseMaxWords,aiEnabled,maxUsers,maxRowsPerUser
runWorkflow return value
result— return value of the workflowentryfunctionstate— final shared workflow stateexecutionId— present whenobservability: true
Links
- Product: dnotifier.com
- Documentation: Product docs
