/ Docs

Migration Guide

Move from a traditional chat provider to Heyyo with minimal disruption. This guide maps common concepts, explains the authentication model (API key + secret → JWT), and provides a practical checklist.

Server credentials api_key + api_secret (backend only)
Client auth User JWT (REST + WebSocket)
Transport REST + Socket.IO real-time events
Important: Don’t ship your api_secret to browsers or mobile apps. Keep it on your backend and mint short-lived user tokens.

1) Concept mapping

Most chat platforms share the same building blocks. The names differ, but the intent is similar.

Traditional provider
  • App / Project (tenant container)
  • User (often created server-side)
  • Channel / Conversation (room, thread, group)
  • Membership (user ↔ channel relationship)
  • Message (text + optional metadata)
  • Token (sometimes long-lived, sometimes session-based)
Heyyo
  • Application (tenant): created via POST /api/apps
  • User: identified by your user_id (in JWT claims)
  • Channel: created via POST /api/channels (e.g. type: messaging)
  • Membership: join semantics are channel-based (see REST API docs)
  • Message: POST /api/channels/:id/messages
  • User token: short-lived JWT issued by your backend
If your existing provider uses “roles” or “capabilities”, model those rules in your backend and mint JWTs accordingly.

2) Authentication model differences

A common migration pain is auth: older systems often rely on server-signed requests, per-user access tokens, or provider-managed user sessions. Heyyo is intentionally simple:

Heyyo auth in one sentence

Your backend uses api_key + api_secret to mint a user JWT, then clients use that JWT for both REST and WebSocket.

Why it helps during migrations

You can keep your existing login system (email/password, OAuth, SSO). Heyyo only needs a stable user_id and optional profile fields.

POST /api/auth/token
Server creds

Exchange api_key + api_secret + your user_id for a user JWT. This is typically called from your backend after the user signs in.

curl -sS -X POST https://api.heyyo.dev/api/auth/token \
  -H 'Content-Type: application/json' \
  -d '{
    "api_key":"cb_...",
    "api_secret":"cbs_...",
    "user_id":"user-123",
    "display_name":"Jane Doe"
  }'
const resp = await fetch('https://api.heyyo.dev/api/auth/token', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    api_key: process.env.CB_API_KEY,
    api_secret: process.env.CB_API_SECRET,
    user_id: session.user.id,
    display_name: session.user.name,
  }),
});

if (!resp.ok) throw new Error(`Token mint failed (${resp.status})`);
const { token } = await resp.json();
return token;
Clients use Authorization: Bearer <USER_JWT> for REST requests and auth.token for WebSocket connections.

3) API differences (REST + real-time)

Traditional providers often split their surface area into multiple base URLs, separate “admin” vs “client” APIs, or distinct auth schemes. Heyyo keeps the mental model tight:

What changes
  • REST base URL: Heyyo uses a single origin for REST + Socket.IO.
  • Auth header: most endpoints use Authorization: Bearer <USER_JWT>.
  • WebSocket: connect once, then listen for message.new and related events.
Traditional provider (typical)
  • Many endpoints require a server key or request signing.
  • Clients often need separate session tokens or provider-managed login.
  • Real-time might require a distinct product/add-on.
Heyyo
  • Your backend mints user JWTs via /api/auth/token.
  • Clients use the same JWT across REST and Socket.IO.
  • Real-time is first-class: message.new is the default update path.
POST /api/channels
User JWT

Create a channel. In most migrations, this is the first entity you recreate.

curl -sS -X POST https://api.heyyo.dev/api/channels \
  -H 'Authorization: Bearer <USER_JWT>' \
  -H 'Content-Type: application/json' \
  -d '{"name":"general","type":"messaging"}'
POST /api/channels/:channelId/messages
User JWT

Send a message.

curl -sS -X POST https://api.heyyo.dev/api/channels/<CHANNEL_ID>/messages \
  -H 'Authorization: Bearer <USER_JWT>' \
  -H 'Content-Type: application/json' \
  -d '{"text":"Hello from the new system"}'

4) Side-by-side code comparison

The fastest way to migrate is to isolate your existing provider behind a small interface (e.g. sendMessage, watchChannel, listChannels) and swap the implementation.

Send a message

Traditional provider (illustrative)
// Client or server, depending on provider.
// Often requires provider-specific SDK + credential config.

await chat.messages.send({
  conversationId: conversationId,
  text: 'Hello!',
});
Heyyo SDK
import { HeyyoClient } from '@heyyo/sdk';

const cb = new HeyyoClient({
  apiUrl: 'https://api.heyyo.dev',
  token: '<USER_JWT>',
});

await cb.messages.send('<CHANNEL_ID>', 'Hello!');

Subscribe to new messages

Traditional provider (illustrative)
// Providers often have a "watch" call that syncs state.
const sub = chat.watchConversation(conversationId);

sub.on('message', (m) => {
  render(m);
});
Heyyo (Socket.IO)
import { io } from 'socket.io-client';

const socket = io('https://api.heyyo.dev', {
  auth: { token: '<USER_JWT>' },
});

socket.on('message.new', (m) => {
  if (m.channel_id !== '<CHANNEL_ID>') return;
  render(m);
});

5) Key advantages after you switch

Simpler authentication

One server call to mint a JWT, then one token type for everything (REST + WebSocket). No per-request signing or multi-token client setup.

Transparent pricing

Costs are easier to reason about when your app controls token issuance and traffic patterns. You can load test locally and forecast usage.

Easy self-hosting

Run Heyyo on your infrastructure for compliance, data locality, or cost control. The API stays the same.

Clean mental model

Tenants (apps) → users (JWT) → channels → messages. Fewer moving pieces makes migrations and onboarding faster.

6) Step-by-step migration checklist

Use this as a cutover plan (works for both “big bang” and gradual migrations).

Preparation
  • Inventory your existing concepts: users, channels, memberships, message history.
  • Pick a stable user_id scheme (string identifiers you already use are best).
  • Decide what must be migrated (history) vs what can start fresh.
Build the adapter layer
  • Create a small abstraction in your codebase: ChatProvider interface.
  • Implement it with Heyyo using REST + Socket.IO (or @heyyo/sdk).
  • Ensure your UI can handle both providers during a transition period.
Authentication & rollout
  • On login, your backend mints a Heyyo JWT via POST /api/auth/token.
  • Ship the JWT to the client and connect Socket.IO with auth.token.
  • Start with a small cohort (internal users), monitor errors and latency.
Data migration (optional)
  • Export channels and membership data from the old system.
  • Recreate channels in Heyyo, then re-add members.
  • Backfill message history only if you need it for compliance or UX.
Cutover
  • Freeze writes on the old provider (short maintenance window) or dual-write temporarily.
  • Point clients to Heyyo, validate send/receive + unread counts.
  • Remove the old provider once confidence is high and data is verified.
If you want a phased rollout, dual-write messages for a short window: write to the old provider and Heyyo, but read from one source of truth.