Quickstart
5 minGo from zero to receiving live events in your app in under five minutes.
Create a Zokai account
Head to the dashboard and register. It's free — no credit card required.
Once logged in, click + New App and give your app a name. You'll land on the app detail page where you can grab your credentials.
Get your API key and secret
Open the Credentials tab in your app. Copy the API Key (public, safe in the browser) and the API Secret (keep this server-side only).
API Secret in client-side code or public repositories. It's used to sign tokens on your backend.
Install the client SDK
npm install @zokai/client
Connect and subscribe
Add this to your frontend. Replace YOUR_APP_KEY with the key from the dashboard.
import Zokai from '@zokai/client';
// 1. Connect using your app key
const client = new Zokai({ appKey: 'YOUR_APP_KEY' });
// 2. Subscribe to a channel
const channel = client.subscribe('orders');
// 3. Listen for events
channel.on('new-order', (data) => {
console.log('New order:', data);
});
Publish an event from your backend
From your server, call the Zokai REST API to push an event to all subscribers.
// server.js
import Zokai from '@zokai/server';
const zokai = new Zokai({
appKey: process.env.ZOKAI_KEY,
appSecret: process.env.ZOKAI_SECRET,
});
// When a new order is created in your app:
await zokai.publish('orders', 'new-order', {
id: 'ORD-1042',
total: 89.00,
status: 'confirmed',
});
Installation
Client SDK (browser / React Native)
npm install @zokai/client
# or
yarn add @zokai/client
# or
pnpm add @zokai/client
Server SDK — Node.js
npm install @zokai/server
Server SDK — Python
pip install zokai
Server SDK — Go
go get github.com/zokai/zokai-go
Authentication
Zokai uses a two-credential system — a public API Key and a private API Secret.
| Credential | Where to use | Safe in browser? |
|---|---|---|
API Key |
Client SDK initialization, WebSocket URL | ✅ Yes |
API Secret |
Server SDK, REST API requests, token signing | ❌ No — backend only |
How it works
When a client connects to Zokai, it opens a WebSocket using the API Key. For public channels, that's enough. For private channels, the client must authenticate with a signed token generated by your backend:
Browser Your Server Zokai
│ │ │
│── subscribe('private-x') ──▶ │
│ │ │
│◀── GET /auth/zokai ────────│ (your auth endpoint) │
│ │── sign token ────────▶ │
│ │◀─ token ────────────── │
│ │ │
│── zok:auth(token) ─────────────────────────────────▶ │
│◀── zok:auth_success ─────────────────────────────── │
│◀── zok:subscribed ───────────────────────────────── │
private-) do not require authentication beyond the API Key.
Channels
Channels are named streams of events. Any number of clients can subscribe to a channel. Events published to a channel are delivered to all current subscribers.
| Type | Prefix | Auth required | Use case |
|---|---|---|---|
| Public | news, global-feed |
No | Public notifications, live scores, price tickers |
| Private | private- |
Yes — server token | Per-user feeds, DMs, order status, account data |
| Presence | presence- |
Yes — server token | Who's online, collaborative cursors, typing indicators |
Naming rules
- Max 200 characters
- Allowed: letters, numbers, hyphens, underscores, dots
- Use dots for namespacing:
chat.room.42,user.1234.inbox
Events
Events are JSON messages sent over a channel. Each event has a name and optional data payload.
{
"event": "new-order",
"channel": "orders",
"data": {
"id": "ORD-1042",
"total": 89.00,
"status": "confirmed"
}
}
- Event names can be any string — use a consistent convention like
kebab-caseorpast-tense verbs. - Data payload can be any JSON value — object, array, string, number.
- Max payload size: 64 KB.
Presence
Presence channels let you track which users are currently subscribed. When a user joins or leaves, all other subscribers in the channel are notified automatically.
const ch = client.subscribe('presence-chat');
// Fires when the subscription succeeds — contains the current member list
ch.on('zokai:subscription_succeeded', ({ members }) => {
console.log('Online now:', members);
});
// Member joined
ch.on('zokai:member_added', ({ id, info }) => {
console.log(info.name, 'joined');
});
// Member left
ch.on('zokai:member_removed', ({ id }) => {
console.log(id, 'left');
});
Client SDK
Installation
npm install @zokai/client
Works in any modern browser and React Native. No additional bundler config needed.
Connecting
import Zokai from '@zokai/client';
const client = new Zokai({
appKey: 'YOUR_APP_KEY', // required
authEndpoint: '/api/zokai/auth', // required for private/presence channels
cluster: 'us-east-1', // optional, default: nearest
});
// Connection lifecycle events
client.on('connected', () => console.log('Connected'));
client.on('disconnected', () => console.log('Disconnected — reconnecting…'));
client.on('error', (err) => console.error('Error', err));
| Option | Type | Default | Description |
|---|---|---|---|
appKey | string | required | Your public API key |
authEndpoint | string | /zokai/auth | Your server endpoint for signing private channel tokens |
cluster | string | auto | Region hint for nearest gateway node |
autoReconnect | boolean | true | Reconnect automatically on disconnect |
Subscribing to channels
// Public channel — no auth required
const feed = client.subscribe('live-feed');
// Private channel — token fetched from your authEndpoint
const inbox = client.subscribe('private-user.1234.inbox');
// Unsubscribe when done
client.unsubscribe('live-feed');
Listening to events
const ch = client.subscribe('orders');
// Bind a specific event
ch.on('order-created', (data) => {
console.log('New order:', data.id);
});
// Bind all events on the channel
ch.onAll((eventName, data) => {
console.log(eventName, data);
});
// Remove a listener
const handler = (data) => { /* ... */ };
ch.on('order-created', handler);
ch.off('order-created', handler);
Presence channels
Presence channels require your backend's authEndpoint to include user identity in the signed token.
const room = client.subscribe('presence-chat.room.42');
// Once subscribed, you get the full member list
room.on('zokai:subscription_succeeded', ({ count, members }) => {
renderUserList(members);
});
// Live updates
room.on('zokai:member_added', ({ id, info }) => addUser(id, info));
room.on('zokai:member_removed', ({ id }) => removeUser(id));
// Access current members at any time
console.log(room.members);
Private channels
When a client subscribes to a private- channel, the SDK calls your authEndpoint to fetch a signed token. Your server verifies the user is authorized and returns the token.
Your auth endpoint (Node.js example)
// Express route — POST /api/zokai/auth
app.post('/api/zokai/auth', requireLogin, (req, res) => {
const { channel, socket_id } = req.body;
// Authorize: only allow users to access their own inbox
if (channel !== `private-user.${req.user.id}.inbox`) {
return res.status(403).json({ error: 'Forbidden' });
}
const token = zokai.auth({ channel, socket_id, userId: req.user.id });
res.json({ token });
});
Client API reference
Zokai instance
| Method | Description |
|---|---|
subscribe(channel) | Subscribe to a channel. Returns a Channel object. |
unsubscribe(channel) | Unsubscribe and clean up listeners. |
on(event, fn) | Listen to connection lifecycle events: connected, disconnected, error. |
disconnect() | Close the WebSocket connection. |
reconnect() | Manually reconnect. |
Channel object
| Method / Property | Description |
|---|---|
on(event, fn) | Listen for a named event on this channel. |
off(event, fn) | Remove a listener. |
onAll(fn) | Listen for all events on this channel. |
name | The channel name string. |
members | Current member map (presence channels only). |
Server SDK — Node.js
Setup
import Zokai from '@zokai/server';
const zokai = new Zokai({
appKey: process.env.ZOKAI_KEY,
appSecret: process.env.ZOKAI_SECRET,
});
Publish an event
// Single channel
await zokai.publish('orders', 'order-created', {
id: 'ORD-1042', total: 89.00,
});
// Multiple channels at once
await zokai.publish(['orders', 'analytics'], 'order-created', { id: 'ORD-1042' });
Generate an auth token
// Use in your auth endpoint to sign channel tokens
const token = zokai.auth({
channel: 'private-user.1234.inbox',
socketId: req.body.socket_id,
userId: 'user-1234',
userInfo: { name: 'Jane Doe' }, // shown in presence member list
});
Server SDK — Python
import zokai, os
client = zokai.Client(
key=os.environ["ZOKAI_KEY"],
secret=os.environ["ZOKAI_SECRET"],
)
# Publish an event
client.publish(
channel="orders",
event="order-created",
data={"id": "ORD-1042", "total": 89.00},
)
# Generate an auth token (for Flask/Django auth endpoints)
token = client.auth(
channel="private-user.1234.inbox",
socket_id=request.form["socket_id"],
user_id="user-1234",
)
Server SDK — Go
import zokai "github.com/zokai/zokai-go"
client := zokai.New(zokai.Config{
Key: os.Getenv("ZOKAI_KEY"),
Secret: os.Getenv("ZOKAI_SECRET"),
})
// Publish an event
err := client.Publish(ctx, zokai.Event{
Channel: "orders",
Name: "order-created",
Data: map[string]any{"id": "ORD-1042", "total": 89.00},
})
// Generate a channel auth token
token, err := client.Auth(ctx, zokai.AuthParams{
Channel: "private-user.1234.inbox",
SocketID: r.FormValue("socket_id"),
UserID: "user-1234",
})
REST API
Base URL: https://api.zokai.io/v1
Authentication
All REST API requests must include your API Key and API Secret as HTTP Basic Auth:
curl https://api.zokai.io/v1/apps/YOUR_APP_KEY/events \
-u YOUR_APP_KEY:YOUR_APP_SECRET \
-H "Content-Type: application/json" \
-d '{"channel":"orders","event":"order-created","data":{"id":"ORD-1042"}}'
Publish event
/apps/{appKey}/events
Publishes an event to one or more channels. All subscribers receive it instantly.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
channel | string | Yes* | Target channel name |
channels | string[] | Yes* | Publish to multiple channels at once |
event | string | Yes | Event name |
data | any | No | JSON payload, max 64 KB |
* Provide either channel or channels.
{
"channel": "orders",
"event": "order-created",
"data": {
"id": "ORD-1042",
"total": 89.00,
"status": "confirmed"
}
}
{ "ok": true, "delivered_to": 24 }
Errors
All error responses follow the same shape:
{
"ok": false,
"error": "Channel name is required",
"code": 400
}
| Status | Meaning |
|---|---|
400 | Bad request — missing or invalid fields |
401 | Authentication failed — check your key and secret |
403 | Forbidden — your plan does not allow this operation |
404 | App not found |
413 | Payload too large — max 64 KB |
429 | Rate limit exceeded |
500 | Internal server error — contact support |