livqenoDocs

ChatQuickstart

Chat Quickstart

Install, create a conversation, connect, and send your first message, on every SDK.

In this guide you'll build a small support chat. A customer, alice, and an agent, bob, talk in a conversation called support-room-42. Each sees the other's messages arrive live.

Chat follows the same pattern as every Livqeno product: your backend holds the API key and sets things up, and your app connects with a short-lived token. If you haven't seen that pattern yet, read What is Livqeno? first. It takes five minutes.

What you need:

  • A Livqeno project and an API key. See API credentials.
  • A backend (TypeScript, Python, Go, Java or PHP) that knows which user is logged in.
  • An app: web, React, React Native, or Flutter.

The plan:

StepWhere it runsWhat happens
1. InstallBothAdd the SDKs.
2. Create a conversationBackendMake support-room-42 and add Alice and Bob as members.
3. Make a tokenBackendMake a chat token for the logged-in user.
4–6. Connect, send, receiveAppJoin the conversation and chat.
7. DisconnectAppClose the connection cleanly.

1. Install

Your backend needs a server SDK: @livqeno/server (TypeScript), livqeno (Python), or the Go, Java or PHP SDK. See Install an SDK. Your app needs one of these:

pnpm add @livqeno/chat

2. Create a conversation (once, from your backend)

A conversation is where messages live. Think of it as one chat thread: a support ticket, a team channel, or the chat next to a call.

Only members of a conversation can read or send in it. So you add the people who should be able to chat when you create it. Here, Alice is the ADMIN and Bob is a normal member.

On your backend:

import { Livqeno } from '@livqeno/server';
const livqeno = new Livqeno({
  apiKey: process.env.LIVQENO_API_KEY,
  baseUrl: process.env.LIVQENO_API_URL, // https://api.livqeno.com
});
 
const conversation = await livqeno.chat.createConversation({
  name: 'support-room-42',
  getOrCreate: true, // safe to re-run: returns the existing one (members apply only on first create)
  members: [{ userId: 'alice', role: 'ADMIN' }, { userId: 'bob' }],
});

You get back the conversation. Its publicId (like conv_9WcQ…) is Livqeno's id for it. You can also refer to it by its name.

Your backend may run this code more than once, for example every time a user opens the chat. Without getOrCreate: true (Python: get_or_create=True), a second create with the same name fails with 409. With it, you get the existing conversation back. Its members are not applied again, so add a new user with addMember instead. More in Conversations.

3. Authenticate — mint a token per user

Your app can't use the API key, so your backend gives it a chat token instead. A chat token says "this is Alice, and she may chat in these conversations, until this time". It expires after 1 hour by default.

On your backend:

const token = await livqeno.chat.createToken({
  userId: 'alice',
  conversations: [conversation.publicId],
});

The response is called a grant. It holds token (the token itself), chatUrl (where to connect), and apiUrl (where to load history). Send it to your app unchanged, from your own endpoint.

In a real app, userId comes from your login session, not a hard-coded string. Here's what that endpoint looks like on your backend:

// app/api/chat/token/route.ts — your backend, holding LIVQENO_API_KEY
export async function POST() {
  const grant = await livqeno.chat.createToken({ userId: session.userId });
  return Response.json(grant);          // token + chatUrl + apiUrl
}

Leaving out conversations lets the token reach every conversation the user is a member of.

Your app then fetches the grant and passes it straight to the SDK. It needs no Livqeno settings of its own. No API key, no URLs:

const grant = await fetch('/api/chat/token', { method: 'POST' }).then((r) => r.json());
const chat = createChatClient(grant);   // forwarded whole, unmodified
await chat.connect();

Because the addresses come inside the grant, the same app code works in development, staging, and production.

4. Connect from the client

Connecting opens one live connection (a WebSocket) to Livqeno and joins the conversation you name. Once connect() finishes, you can send.

Pass the conversation you want with room. The first one you join becomes the default for sendMessage(). A client that joins no conversation receives nothing, and sending fails with NOT_IN_ROOM.

In your app:

import { createChatClient } from '@livqeno/chat';
 
const chat = createChatClient(token); // the whole grant from step 3
await chat.connect({ room: conversation.publicId });

Notice that some examples use the publicId and some use the name support-room-42. Both work. See Conversation references.

5. Send a message

Sending is one call. It finishes only after Livqeno has saved the message, so a finished call means the message is safe.

In your app:

await chat.sendMessage({ text: 'Hello everyone!' });

You don't set who sent it. The sender is always the user in the token, so Alice can't post as Bob.

6. Receive messages

New messages arrive as events. Listen once, then add each message to your UI.

In your app:

chat.on('message', (message) => console.log(message.senderId, message.text));

You get your own messages back too. Show those, instead of a local copy, so everyone sees the same messages in the same order.

7. Disconnect

Disconnect when the user leaves the chat screen. Others then see them go offline right away, instead of after a timeout.

await chat.disconnect();

Done! Open the app as Bob (make a token with userId: 'bob'), and the two of you can chat.

What happens behind the scenes

When Alice sends "Hello everyone!":

  1. Her app sends the message over its connection.
  2. Livqeno saves it and gives it an id and a time.
  3. Her sendMessage() call finishes.
  4. Livqeno delivers it to every connected member, including Alice, as a message event.
  5. If Bob is offline, nothing is lost. He sees it in history when he comes back.

If Alice's connection drops, the SDK reconnects and catches up on missed messages for you.

Who does what

Livqeno handlesYou handle
The WebSocket connectionMaking tokens on your backend, for the logged-in user
Reconnecting and catching up on missed messagesDeciding who is a member of each conversation
Message order and savingThe UI: messages, typing, presence
Safe retries (no duplicates)

Common errors

ErrorCauseFix
409 when creating the conversationIt already exists with that name.Pass getOrCreate: true.
404 / ROOM_NOT_FOUND on connect or first sendThe user isn't a member, or the conversation has no members.Add them with addMember before making their token. Check pendingMembership.
NOT_IN_ROOM on sendYou didn't join a conversation in connect(), and didn't pass room.Pass { room } to connect().
chat:send scope missingThe token or the user's role doesn't allow sending.Check the role. See Members.
Message never reaches other usersThe server refused it. Refusals don't come back as message events.Listen for error too. See Troubleshooting.
senderId is ignoredAn app with a chat token can't set it.This is expected. See Messages.
ORIGIN_NOT_ALLOWED, socket closes with 4403Your site's address isn't on the project's allowed list. The token is fine.Add it under Project Settings → Security → Allowed Origins. Retrying won't help. localhost is allowed by default. See Browser security.
403 with RAVEN_PERMISSION_DENIED on a Chat REST callSame cause, on the REST API.Same fix. You get a real 403 you can read, not a hidden CORS error.

Production tips

  • Never call livqeno.chat.createConversation() / createToken() (or any @livqeno/server / livqeno method) from a browser or app.
  • Take userId from your own login session. A token for the wrong user lets them send as someone else.
  • The SDK adds a clientMessageId for you, so its own retries are safe. Pass your own only when you retry (a job queue, an offline outbox).

Next steps

Your chat works. Now make it feel like a real chat app:

  1. Message history: show past messages when the chat opens, with a "Load earlier" button.
  2. Members: add and remove people, and give moderators their role.
  3. Typing, Presence, and Read receipts: show "Bob is typing…", who's online, and "Seen".
  4. Need a video call too? See RTC. Chat and RTC work independently, and you can link them with a roomId.