Developer docs
Works with React, Next.js, Vue or plain HTML. This demo uses Vite + React Router. These are the essentials; the Quokra repo has the full reference.
Integrate in minutes
1.Add the chat to your site
One script tag on any page of your website.
<script src="https://chat.example.com/sdk.js" data-quokra-chat="your-slug" data-quokra-pk="pk_live_…" async></script>2.Add the console to your dashboard
Mount the full agent console on a page of your dashboard:
<div id="support" style="height: 100vh"></div> <script src="https://chat.example.com/sdk.js"></script> <script> Quokra.console.mount("#support", { tenant: "your-slug", getToken: () => auth.getAccessToken(), // your dashboard's login }); </script>Or a floating button with a side drawer on every page:
Quokra.console.init({ mode: "bubble", tenant: "your-slug", getToken: () => auth.getAccessToken(), fullView: (id) => router.push(id ? `/support?conversation=${id}` : "/support"), });3.Identify logged-in customers (optional)
On your backend, sign a short token with your secret key:
import { SignJWT } from "jose"; const token = await new SignJWT({ name: user.name, email: user.email }) .setProtectedHeader({ alg: "HS256" }) .setSubject(user.id) .setIssuedAt() .setExpirationTime("5m") .sign(new TextEncoder().encode(process.env.QUOKRA_SECRET_KEY));Then in the page:
Quokra.chat.identify(token); // after login Quokra.chat.logout(); // on logout
Guides
Step by step, from running it locally to notifications. Each links to the full guide in the Quokra repo.
1.Get started
Run Quokra, PostgreSQL and a demo login with Docker, and try both sides of a chat.
- docker compose up --build in the Quokra repo
- Chat as a customer at localhost:8080/w/demo?pk=pk_test_demo
- Answer as sam / sam from this playground's Support page
2.Set up a company
Admins create each company in the console's Admin tab and get its keys and embed code.
- Admin → New company: slug, name, colour, bot file, when quiet chats close
- List the websites (chat) and dashboards (console) allowed to embed it
- Copy the publishable key and the ready-made embed snippets
- Add agents by work email; create the secret key if you identify customers
3.Add the chat to a website
One script tag for a bubble, or inline and popup placements.
- Paste the script tag before </body>
- Optional: data-quokra-theme, data-quokra-accent, data-quokra-position
- Listen for unavailable to show your own contact options if Quokra is down
4.Add the console to a dashboard
A full Support page, or a button with a side drawer on every page.
- Quokra.console.mount on your Support page, with getToken from your login
- Or Quokra.console.init({ mode: "bubble" }) in your layout, with fullView
- Pass notifications: true for sounds, toasts and browser notifications
5.Identify logged-in customers
Agents see who they're talking to; customers keep one chat across devices.
- Your backend signs a 5-minute HS256 token with the company's secret key
- The page calls Quokra.chat.identify(token) after login
- Call Quokra.chat.logout() when they log out
6.Connect your identity provider
Agents sign in with your company login: Keycloak, Auth0, Okta, Entra ID or your own.
- Roles SUPPORT_AGENT and QUOKRA_ADMIN, audience quokra-api in access tokens
- Set OIDC_ISSUER_URI (and the roles claim) on the Quokra server
- Check with GET /api/v1/agent/me: 200 is good, 401 bad token, 403 missing role
7.Write the bot
A YAML file per company: greeting, menu, keywords, urgent words and wording.
- Copy the demo bot file and point QUOKRA_BOTS_DIR at your folder
- Write the menu answers and keywords; add urgent words that go straight to the team
- Pick the file for the company in the Admin tab
8.Notifications
New chats, replies in my chats, transfers and notes, each with its own sound.
- The dashboard passes notifications: true
- Agents click Turn on once to allow browser notifications
- The bell menu chooses what notifies each agent, and how
API at a glance
JavaScript SDK (sdk.js)
| Call | What it does |
|---|---|
| Quokra.chat.init({ slug, pk, mode, theme, accent }) | Show the chat (bubble, inline or popup) |
| Quokra.chat.open() / close() | Open or close it |
| Quokra.chat.identify(token) / logout() | Link to or forget the logged-in customer |
| Quokra.chat.on("unread", n => …) | Events: ready, open, close, unread, identified |
| Quokra.console.mount(el, { tenant, getToken }) | Full-page agent console |
| Quokra.console.init({ mode: "bubble", … }) | Console in a drawer from a floating button |
| Quokra.console.on("unread", n => …) | Waiting chats plus unread in my chats, e.g. for a nav badge |
| notifications: true | Console option: sounds, toasts and browser notifications for agents |
REST API (/api/v1)
| Endpoint | Who | What |
|---|---|---|
| POST /widget/sessions | Customer chat | Start a guest session |
| POST /widget/sessions/identify | Customer chat | Switch to a logged-in customer |
| POST /widget/conversation/messages | Customer chat | Send a message |
| GET /agent/conversations?status=WAITING | Agents | Queue (paged, searchable) |
| POST /agent/conversations/{id}/accept | Agents | Take a conversation |
| POST /agent/conversations/{id}/messages | Agents | Reply |
| POST /agent/conversations/{id}/resolve | Agents | Close it |
Realtime (WebSocket, STOMP)
Make it yours
- Theme
- Follows your page's light or dark mode automatically, or force light, dark or auto.
- Brand colour
- Set per company, or pass accent: "#0f766e" from your page.
- Wording
- Greeting, menu, answers, header status and the bot's questions come from a per-company file. No code change.
- Placement
- Bottom right or left, inline in your page, or a popup from your own button.
Security
- Each site that may embed the chat or console is registered, and the browser blocks everyone else (frame-ancestors).
- Agents sign in through your identity provider; Quokra checks the token, the role and team membership on every request.
- Customer identity is signed by your backend with a secret that never reaches the browser, and tokens live at most 5 minutes.
- Rate limits, blocked contacts, and messages always shown as plain text.
See it working before you integrate.
Try the demo