/ Docs

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.

Production URL https://heyyo-production-c881.up.railway.app
Local URL http://localhost:4000
WebSocket Socket.IO (same origin as REST)
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);
});