Quickstart
Documentation
Quickstart
From install to your first real-time message in about five minutes.
emb.chat splits cleanly into two halves: your backend signs chat tokens and runs admin operations with the Server SDK, and your frontend (browser, React Native, or Node) connects and exchanges messages with the Client SDK. This guide wires up both.
Install
Install whichever packages match where your code runs.
# backend — token signing + admin
npm install @emb-chat/server-sdk
# frontend — the universal chat client
npm install @emb-chat/react-native-sdk
# optional — the admin CLI
npm install -g @emb-chat/cli
The Client SDK is dependency-light (just
socket.io-client) and runs unchanged in browsers, React Native, and Node 20+.
1. Generate and register a signing key
emb.chat verifies tokens against a public key you register once. Generate an RS256 or EdDSA keypair, keep the private key on your backend, and upload the public half.
# generate ./emb-private.pem and ./emb-public.pem
emb keys generate
# register the public key under a key id (kid)
emb keys register --key ./emb-public.pem --kid key_01
You can do the same from code with ChatAuth.generateKeyPair('RS256') and admin.keys.register(...) — see the Server SDK and CLI references.
2. Sign a token on your backend
Your backend holds the private key and signs a short-lived token for the logged-in user. This happens locally — there is no round-trip to emb.chat.
import { readFileSync } from 'node:fs';
import { ChatAuth } from '@emb-chat/server-sdk';
const privateKey = await ChatAuth.loadPrivateKey(
readFileSync('./emb-private.pem', 'utf8'),
);
export async function mintChatToken(user) {
return ChatAuth.createToken(privateKey, {
sub: user.id, // your user id
aud: 'app_demo', // your app id
kid: 'key_01', // matches the registered public key
name: user.displayName, // optional — mirrored into the chat profile
meta: { role: user.role },
expiresIn: '1h',
});
}
Send that token to your frontend through your normal session endpoint.
3. Connect a client
import { ChatClient } from '@emb-chat/react-native-sdk';
const chat = new ChatClient({
url: 'https://chat.acme.com',
token, // the token your backend just minted
});
await chat.connect(); // resolves once the socket is ready
4. Send and receive your first message
// list the channels this user can see
const channels = await chat.channels.list();
const channel = chat.channel(channels[0].id);
// subscribe to real-time events
channel.on('message:new', (msg) => {
console.log(`${msg.sender.display_name ?? msg.sender_id}: ${msg.body}`);
});
channel.on('typing:update', (t) => {
if (t.typing) console.log(`${t.user_id} is typing…`);
});
// send a message (resolves on the server ack)
await channel.sendMessage({ body: 'Hello, world', type: 'text' });
// load older history when you need it
const page = await channel.getMessages({ limit: 50 });
console.log(page.messages, page.hasMore);
That’s the whole loop: sign on the server, connect on the client, react to events.
Run it locally
The whole stack comes up with one compose command — no external services.
docker compose up -d postgres redis
pnpm -F server db:migrate
pnpm -F server dev # REST + WebSocket gateway
pnpm -F server worker:dev # webhook delivery worker
Next steps
- Server SDK — keypairs, token claims, and the full
ChatAdminsurface. - Client SDK — every channel event, pagination, presence, and typing.
- Webhooks — receive signed, retried events on your own services.
- CLI — manage keys, tokens, channels, and webhooks from your terminal.