Quickstart

First message in six commands

This is the shortest path that proves the stack end to end: an account, a key, a room, a token, a socket, and a message arriving on it. About five minutes.

  1. Create an account Signup provisions your tenant and returns an as_… session for the account plane.
    curl -X POST https://api.example.com/v1/account/signup \
      -H "Content-Type: application/json" \
      -d '{"email":"you@example.com","password":"a-long-passphrase"}'
    # → { "tenant_id": "acct_…", "session_token": "as_…", "expires_at": "…" }
  2. Create a server API key This is the credential your backend uses. It is tenant-pinned, scoped, and shown once — store it somewhere your clients cannot reach.
    curl -X POST https://api.example.com/v1/account/api-keys \
      -H "Authorization: Bearer as_…" \
      -H "Content-Type: application/json" \
      -d '{"name":"backend","environment":"live"}'
    # → { "key_id": "…", "api_key": "ak_live_…"  ← shown once }
  3. Provision a room with two members external_ref makes creation idempotent — run it twice and you get the same room.
    curl -X POST https://api.example.com/v1/server/rooms \
      -H "Authorization: Bearer ak_live_…" \
      -H "Content-Type: application/json" \
      -d '{"created_by":"alice","external_ref":"demo-room"}'
    # → { "id": "0f5c…", … }
    
    for p in alice bob; do
      curl -X POST https://api.example.com/v1/server/rooms/0f5c…/members \
        -H "Authorization: Bearer ak_live_…" \
        -H "Content-Type: application/json" \
        -d "{\"principal_id\":\"$p\",\"role\":\"member\"}"
    done
  4. Mint a room-scoped token per user Do this once per user per room. The response carries both socket URLs and the ICE servers.
    curl -X POST https://api.example.com/v1/server/session-tokens \
      -H "Authorization: Bearer ak_live_…" \
      -H "Content-Type: application/json" \
      -d '{"room_id":"0f5c…","principal_id":"alice"}'
    # → { "access_token": "eyJ…", "chat_websocket_url": "wss://…", "rtc_websocket_url": "wss://…", "ice_servers": [ … ] }
  5. Listen on the chat socket as Bob The token rides the WebSocket subprotocol, never the URL.
    // paste in a browser console, with bob's access_token
    const ws = new WebSocket("wss://api.example.com/v1/chat/ws",
                             ["chatbox", "bearer." + BOB_TOKEN]);
    ws.onmessage = (e) => console.log(JSON.parse(e.data));
    // now run the curl above as alice — bob sees message.created arrive
  6. Send as Alice and watch it arrive You can send over REST (below) or over Alice's own socket — the durability and ordering are the same either way.
    curl -X POST https://api.example.com/v1/rooms/0f5c…/messages \
      -H "Authorization: Bearer eyJ…" \
      -H "Content-Type: application/json" \
      -d '{"client_message_id":"first-1","body":{"text":"hello from curl"}}'
What you just proved. The message was committed and assigned a per-room sequence before Bob saw it. That ordering is what makes gap-free reconnection work — see Chat for catch-up and receipts.

Where to go next #