Whiteboard Quickstart
Create a room, mint a room token, join from the Web SDK and draw your first stroke.
This page gets a whiteboard running in four steps: create a room, mint a room token, join with the Web SDK and draw a stroke, then export a snapshot or a replay.
Prerequisites
- A project with RTC enabled and its
customerId/customerSecret(project keys page in the console). - The control-plane base URL (the examples use
https://api.your-domain.example). - Node.js 22+ or any browser runtime with Fetch.
Step 1: create a room
Call the room endpoint with project credentials. limit is the maximum number of writable members; 0 means unlimited.
curl --request POST "$NIMEET_API_BASE/v1/whiteboard/rooms" \
--user "$NIMEET_CUSTOMER_ID:$NIMEET_CUSTOMER_SECRET" \
--header "content-type: application/json" \
--data '{"isRecord": false, "limit": 8}'The uuid in the response identifies the room.
Step 2: mint a room token
A room token is bound to that room and carries a role and a lifetime in milliseconds (0 means no expiry). The browser should only ever receive the token, never the project secret.
curl --request POST "$NIMEET_API_BASE/v1/whiteboard/tokens/rooms/$ROOM_UUID" \
--user "$NIMEET_CUSTOMER_ID:$NIMEET_CUSTOMER_SECRET" \
--header "content-type: application/json" \
--data '{"role": "writer", "lifespan": 3600000}'Step 3: join and draw
import { WhiteboardSdk } from '@nimeet/sdk-whiteboard-web';
const sdk = new WhiteboardSdk({ apiBase: 'https://api.your-domain.example' });
const room = await sdk.joinRoom({
uuid: ROOM_UUID,
roomToken: ROOM_TOKEN,
memberId: 'alice',
});
await room.addPage({ name: 'page1', path: '/demo/page1' });
await room.setScenePath('/demo/page1');
const stroke = await room.drawStroke({
appliance: 'pencil',
color: '#1f6feb',
width: 3,
points: [
{ x: 120, y: 160 },
{ x: 220, y: 240 },
{ x: 320, y: 180 },
],
});
console.log('stroke', stroke.id, 'room version', room.roomInfo.version);Page switching, image insertion and viewport movement use room.setScenePath, room.insertImage and room.moveCamera; undo and redo are room.undo() / room.redo().
Step 4: export and replay
curl --request GET "$NIMEET_API_BASE/v1/whiteboard/rooms/$ROOM_UUID/snapshot" \
--user "$NIMEET_CUSTOMER_ID:$NIMEET_CUSTOMER_SECRET"
curl --request GET "$NIMEET_API_BASE/v1/whiteboard/rooms/$ROOM_UUID/replay?from=2026-10-28T00:00:00Z" \
--user "$NIMEET_CUSTOMER_ID:$NIMEET_CUSTOMER_SECRET"The snapshot contains every scene, stroke, image and the operation log; replay returns operation frames ordered by time. You can also download <ROOM_UUID>/snapshot.svg as a thumbnail of the current scene.
Realtime sync
The Web SDK publishes operations to the whiteboard topic through an injected LiveKit transport:
import { LiveKitWhiteboardSync, WhiteboardSdk } from '@nimeet/sdk-whiteboard-web';
const sync = new LiveKitWhiteboardSync({
publishData: (payload, options) => room.localParticipant.publishData(payload, options),
});
room.localParticipant.on('dataReceived', (packet) => sync.receive(packet.payload));
const sdk = new WhiteboardSdk({ apiBase: 'https://api.your-domain.example' });
const whiteboard = await sdk.joinRoom({ uuid: ROOM_UUID, roomToken: ROOM_TOKEN, memberId: 'alice', sync });REST remains the authoritative state; if the realtime channel drops a packet, call room.refresh() to re-read the scene state.
Next steps
- Authentication and permissions: Concepts
- Full endpoint tables: Whiteboard REST API
- SDK classes and methods: SDK reference