SDK v1.2.0 — Notifications + Realtime + User System

Developer Docs

Welcome! Everything you need to send push notifications, track delivery, and build realtime applications with Datonow.

smart_toy

Using an AI coding assistant?

Point it at /agents.md — optimized for LLM context

open_in_new

01Install SDKs

npm install @datonow/sdk @datonow/sdk-server
@datonow/sdk

Browser & Node.js client — WebSocket, channels, notification inbox.

@datonow/sdk-server

Server-only — send notifications, trigger events, sign channel auth.

02Send Push Notifications (Server)

Messages are stored in PostgreSQL, delivered instantly over WebSocket, and queued for offline clients.

import { DatonowServer } from '@datonow/sdk-server';

const server = new DatonowServer({
  appId:  process.env.DATONOW_APP_ID!,
  key:    process.env.DATONOW_APP_KEY!,
  secret: process.env.DATONOW_APP_SECRET!, // app secret = broadcast to all tokens
});

// Send a push notification (broadcast)
await server.sendNotification({
  title:    'Deploy Complete',
  message:  'v2.1.0 deployed to production',
  priority: 8,   // 1 (low) – 10 (critical)
  ttl:      3600,
  extras:   { buildId: 1482, url: 'https://ci.example.com/builds/1482' },
});

// getMessages() with app secret → MessageWithStats[] (aggregate delivery counts)
const { messages, nextCursor } = await server.getMessages({ limit: 20 });
// messages[0] = { id, title, priority, totalDeliveries: 3, deliveredCount: 2, readCount: 1 }

// Token management
const token = await server.createClientToken('ios-device-alice');
await server.deleteClientToken(token.id);

03Receive Notifications (Client)

Connect with a client token to enter notification mode. Offline messages are automatically delivered on reconnect (exponential backoff, max 5 attempts).

import { DatonowClient } from '@datonow/sdk';

// Connect with a client token (notification mode)
const client = new DatonowClient({
  token:    process.env.DATONOW_CLIENT_TOKEN!,
  wsHost:   'wss://rt.yourdomain.com',
  httpHost: 'https://rt.yourdomain.com',
});
client.connect();

// Receive realtime push notifications
client.notifications.onNotification((msg) => {
  console.log(`[P${msg.priority}] ${msg.title}: ${msg.message}`);
});

// Inbox history — only messages delivered to THIS token, with per-token status
const { messages, nextCursor } = await client.notifications.getHistory(
  undefined, // cursor — omit for first page
  20         // limit (max 100)
);
// messages[0] = { id, title, priority, status: "pending"|"delivered"|"read", deliveredAt?, readAt? }

await client.notifications.markAsRead(messages[0].id);
await client.notifications.markAllRead();
await client.notifications.delete(messages[0].id);

04Realtime Channels

Subscribe to public, private, or presence channels for live event broadcasting.

Public

my-channel

Anyone with the App Key. No auth needed.

Private

private-*

HMAC-signed auth from your server required.

Presence

presence-*

Like private + tracks online members.

Client

import { DatonowClient } from '@datonow/sdk';

// AppKey mode — pub/sub channels (like Pusher)
const client = new DatonowClient({
  appKey:       process.env.DATONOW_APP_KEY!,
  wsHost:       'wss://rt.yourdomain.com',
  authEndpoint: '/api/datonow/auth', // required for private/presence
});
client.connect();

// Public channel (no auth needed)
const ch = client.subscribe('public-updates');
await ch.subscribe();
ch.bind('news_alert', (data) => console.log('Event:', data));

// Presence channel (tracks who is online)
const presence = client.subscribe('presence-room-42');
await presence.subscribe({ userId: 'u-123', userInfo: { name: 'Alice' } });
presence.bind('datonow:member_added',   (m) => console.log('Joined:', m));
presence.bind('datonow:member_removed', (m) => console.log('Left:', m));

Server

import { DatonowServer } from '@datonow/sdk-server';

const server = new DatonowServer({
  appId:  process.env.DATONOW_APP_ID!,
  key:    process.env.DATONOW_APP_KEY!,
  secret: process.env.DATONOW_APP_SECRET!,
});

// Trigger event on one channel
await server.trigger('public-updates', 'news_alert', { message: 'Hello!' });

// Trigger on multiple channels at once
await server.trigger(['room-1', 'room-2'], 'announcement', { text: 'Maintenance in 5 min' });

// Exclude sender socket (avoid echo)
await server.trigger('chat', 'message_sent', data, { socketId: senderSocketId });

05Private & Presence Auth Endpoint

When a client subscribes to a private-* or presence-* channel, the SDK POSTs to your authEndpoint with the socket ID and channel name. Your server verifies the session and returns an HMAC signature.

// app/api/datonow/auth/route.ts (Next.js App Router)
import { NextRequest, NextResponse } from 'next/server';
import { DatonowServer } from '@datonow/sdk-server';
import { getSession } from '@/lib/session'; // your auth helper

const server = new DatonowServer({
  appId:  process.env.DATONOW_APP_ID!,
  key:    process.env.DATONOW_APP_KEY!,
  secret: process.env.DATONOW_APP_SECRET!,
});

export async function POST(req: NextRequest) {
  const session = await getSession();
  if (!session) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });

  const { socket_id, channel_name } = await req.json();

  // Presence channel — includes user identity (member list)
  if (channel_name.startsWith('presence-')) {
    const auth = server.authenticatePresenceChannel(
      socket_id, channel_name,
      session.userId, { name: session.userName }
    );
    return NextResponse.json(auth); // { auth, channel_data }
  }

  // Private channel — just sign it
  if (channel_name.startsWith('private-')) {
    return NextResponse.json(server.authenticatePrivateChannel(socket_id, channel_name));
  }

  return NextResponse.json({ error: 'Unknown channel type' }, { status: 400 });
}

06SaaS Integration — User-Targeted Notifications

The key pattern for integrating Datonow into your own product: one client token per end-user. When you authenticate with a client token, delivery is scoped to that token only. When you authenticate with the app secret, it broadcasts to all tokens in the app.

key

Auth mode determines delivery scope

secret: appSecret → broadcast to all tokens · secret: userToken → targeted to one user

// Step 1 — On user signup in YOUR app, create a Datonow token for them
const mgmt = new DatonowServer({ appId, key, secret: process.env.DATONOW_APP_SECRET! });
const token = await mgmt.createClientToken(`user-${user.id}`);
await db.users.update({ where: { id: user.id }, data: { datonowToken: token.token } });

// Step 2 — When a background job finishes, notify that specific user
const user = await db.users.findUnique({ where: { id: jobResult.userId } });
const targeted = new DatonowServer({
  appId, key,
  secret: user.datonowToken,  // ← user's personal token, NOT the app secret
});
await targeted.sendNotification({
  title:   'Your export is ready',
  message: 'The CSV you requested has finished processing.',
  priority: 7,
  extras:  { downloadUrl: 'https://...' },
});

// Step 3 — Connect from the browser (each user connects with their own token)
const { token } = await fetch('/api/me/notifications-token').then(r => r.json());
const client = new DatonowClient({ token, wsHost, httpHost });
client.connect();
client.notifications.onNotification((msg) => showToast(msg.title, msg.message));

07REST API Reference

Notification API

X-Datonow-App-Id + X-Datonow-Token
POST/api/message
GET/api/messages?cursor=&limit=
DELETE/api/messages/{id}
DELETE/api/messages
POST/api/messages/{id}/delivered
POST/api/messages/{id}/read
POST/api/messages/read-all
GET/api/apps/{appId}/tokens
POST/api/apps/{appId}/tokens
DELETE/api/apps/{appId}/tokens/{id}

Realtime API

X-Datonow-Signature (HMAC)
GET/ws?token=
GET/ws?appKey=
POST/api/trigger
POST/api/subscribe
POST/api/unsubscribe
speed10 conn/s per IP WebSocket connections
speed600 req/min per app Trigger / message API
smart_toy

Using Cursor, Copilot, or Claude?

/agents.md is a plain-text integration guide optimized for AI coding assistants — accurate method signatures, type definitions, auth scoping rules, and copy-paste patterns.

downloadagents.md