Quickstart
A copy/paste path from zero to real-time chat: create an app, mint a user token, create a channel, then send messages via REST and WebSocket.
Demo note:
POST /api/apps is unauthenticated in this codebase. In
production, create apps via a dashboard or admin-only flow.
Step 1 — Create an app
Create a tenant and store the returned api_key + api_secret.
The secret is only shown once.
curl -sS -X POST https://heyyo-production-c881.up.railway.app/api/apps \
-H 'Content-Type: application/json' \
-d '{"name":"Acme Chat"}'
Step 2 — Mint a user token
Call this from your backend. The response includes a user JWT you’ll use for REST and WebSocket connections.
curl -sS -X POST https://heyyo-production-c881.up.railway.app/api/auth/token \
-H 'Content-Type: application/json' \
-d '{
"api_key":"cb_...",
"api_secret":"cbs_...",
"user_id":"user-123",
"display_name":"Jane Doe"
}'
Step 3 — Create a channel
curl -sS -X POST https://heyyo-production-c881.up.railway.app/api/channels \
-H 'Authorization: Bearer <USER_JWT>' \
-H 'Content-Type: application/json' \
-d '{"name":"general","type":"messaging"}'
Step 4 — Send a message (REST)
curl -sS -X POST https://heyyo-production-c881.up.railway.app/api/channels/<CHANNEL_ID>/messages \
-H 'Authorization: Bearer <USER_JWT>' \
-H 'Content-Type: application/json' \
-d '{"text":"Hello from REST"}'
Step 5 — Send + receive messages (WebSocket)
import { io } from 'socket.io-client';
const socket = io('https://heyyo-production-c881.up.railway.app', {
auth: { token: '<USER_JWT>' },
});
socket.on('connect', () => console.log('connected', socket.id));
socket.on('message.new', (msg) => {
console.log('message.new', msg.id, msg.text);
});
socket.emit('message.send', { channelId: '<CHANNEL_ID>', text: 'Hello from WS' }, (ack) => {
if (ack?.error) console.error('send failed', ack.error);
});
Full working Node.js example (copy/paste)
Requires Node 18+. You’ll install socket.io-client and run a single
script.
mkdir cb-quickstart && cd cb-quickstart
npm init -y
npm i socket.io-client
export CB_API_KEY='cb_...'
export CB_API_SECRET='cbs_...'
# optional
export CB_BASE_URL='https://heyyo-production-c881.up.railway.app'
node quickstart.mjs
import { io } from 'socket.io-client';
const BASE_URL = process.env.CB_BASE_URL ?? 'https://heyyo-production-c881.up.railway.app';
async function json(res) {
const text = await res.text();
return text ? JSON.parse(text) : null;
}
async function assertOk(res) {
if (res.ok) return;
const data = await json(res).catch(() => null);
const msg = data?.error || data?.message || `Request failed (${res.status})`;
throw new Error(msg);
}
async function main() {
const apiKey = process.env.CB_API_KEY;
const apiSecret = process.env.CB_API_SECRET;
if (!apiKey || !apiSecret) throw new Error('Missing CB_API_KEY / CB_API_SECRET');
// 1) Issue user JWT
const tokenRes = await fetch(`${BASE_URL}/api/auth/token`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
api_key: apiKey,
api_secret: apiSecret,
user_id: 'user-123',
display_name: 'Jane Doe',
}),
});
await assertOk(tokenRes);
const { token } = await json(tokenRes);
// 2) Create channel
const channelRes = await fetch(`${BASE_URL}/api/channels`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`,
},
body: JSON.stringify({ name: 'general', type: 'messaging' }),
});
await assertOk(channelRes);
const channel = await json(channelRes);
// 3) Send message via REST
const msgRes = await fetch(`${BASE_URL}/api/channels/${channel.id}/messages`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`,
},
body: JSON.stringify({ text: 'Hello from REST' }),
});
await assertOk(msgRes);
const msg = await json(msgRes);
console.log('REST message created:', msg.id);
// 4) Connect WebSocket and send a message
const socket = io(BASE_URL, { auth: { token } });
socket.on('connect', () => {
console.log('WS connected:', socket.id);
socket.emit('message.send', { channelId: channel.id, text: 'Hello from WebSocket' }, (ack) => {
if (ack?.error) console.error('WS send error:', ack.error);
else console.log('WS message ack:', ack.message?.id);
});
});
socket.on('message.new', (m) => {
if (m.channel_id !== channel.id) return;
console.log('WS message.new:', m.id, m.text);
});
socket.on('connect_error', (err) => {
console.error('WS connect_error:', err.message);
});
setTimeout(() => {
socket.disconnect();
console.log('done');
}, 3500);
}
main().catch((e) => {
console.error(e);
process.exit(1);
});
SDK usage (@heyyo/sdk)
import { HeyyoClient } from '@heyyo/sdk';
const client = new HeyyoClient({
apiUrl: 'https://heyyo-production-c881.up.railway.app',
token: '<USER_JWT>',
});
const channel = await client.channels.create('general', { type: 'messaging' });
const message = await client.messages.send(channel.id, 'Hello from the SDK');
client.on('message.new', (m) => {
console.log('message.new', m.id);
});