Web JavaScript SDK v0.1.0 JS

The Keme JavaScript SDK adds drop-in in-app player support — tickets and a knowledge base — to any web game or app. It works with React, Vue, Angular, Svelte, or plain vanilla JavaScript, ships with first-class TypeScript types, and is the reference implementation that the native SDKs (Unity, Android, iOS) mirror exactly.

Overview

The SDK wraps the Keme player-portal REST API at https://api.kemegames.com/api/v1 and handles token persistence, the { success, data } response envelope, and typed errors for you. It exposes a small, consistent surface: auth, tickets, knowledge base, and an optional drop-in widget.

    Drop-in support widget — zero custom UI required (openSupport())Auto-provisioning login — first login creates the player, no separate registrationFull ticket lifecycle — create, list, get, reply, rateKnowledge base — search, categories, featured, article-by-slugFull TypeScript type definitions includedWorks from any origin — the player-portal API allows cross-origin calls (WebGL builds included)
Note:The SDK works in the browser and in any modern JS runtime. In the browser, the token is persisted to localStorage by default; in Node it falls back to in-memory storage. The drop-in widget (openSupport()) is browser-only and is a no-op elsewhere.

Installation

Install @keme/sdk with your preferred package manager. The source lives in the keme-sdk monorepo under /js.

bash
1# npm
2npm install @keme/sdk
3
4# yarn
5yarn add @keme/sdk
6
7# pnpm
8pnpm add @keme/sdk

Initialization

Create a client with Keme.init(). The gameId identifies your game/app in Keme and is used when tickets are filed. appName sets the header shown in the drop-in widget.

main.ts
1import { Keme } from '@keme/sdk';
2
3// Create a client. gameId identifies your game/app in Keme.
4const keme = Keme.init({
5 gameId: 'your-game-id',
6 appName: 'Your Game Support', // shown in the drop-in widget header
7});
Note:Store your gameId in environment variables. In Next.js, prefix with NEXT_PUBLIC_ so it is available client-side. The gameId is not a secret — it identifies your game, and all player data access is gated by the player's session token.

Player Login

After your own authentication flow completes, call keme.login(). The userId is your player's unique id in your game and maps to the portal gameUid. The first login auto-provisions the player — there is no separate registration step. The SDK persists the returned bearer token for you.

typescript
1// After your own auth completes, log the player into Keme.
2// The first login auto-provisions the player — no separate registration.
3await keme.login({
4 userId: player.id, // your player's unique id (maps to gameUid)
5 username: player.name, // optional — used when the player is first provisioned
6 email: player.email, // optional
7});
8
9// Later, when the player signs out of your game:
10keme.logout();
11
12// Check auth state at any time:
13if (keme.isLoggedIn()) {
14 const me = await keme.getPlayer();
15 console.log('Signed in as', me.username, '· VIP tier', me.vipTier);
16}
    userId is required and must be stable and unique per player in your gameusername and email are optional and used only when the player is first provisionedlogin() returns the Player object (id, username, gameUid, vipTier, and more)A banned player's login is rejected by the platform (403)
Warning:Never pass passwords, payment card data, or other sensitive fields to the SDK. Pass only the non-sensitive identifiers needed to provision and identify the player.

Drop-in Support Widget

Once a player is logged in, call keme.openSupport() to mount the drop-in widget. It renders the player's ticket list, a compose screen, and the knowledge base — no custom UI required. Wire it to any button in your game HUD or navigation.

typescript
1// Open the drop-in support widget (browser only).
2// Renders the ticket list, compose screen, and knowledge base — no custom UI required.
3keme.openSupport();

React

Create the client in a useEffect, keep a ref to it, and call openSupport() from your own control.

SupportButton.tsx
1// React — initialize once and open support from your own button.
2import { useEffect, useRef } from 'react';
3import { Keme, type KemeClient } from '@keme/sdk';
4import { useAuthStore } from '@/store/auth.store';
5
6export function SupportButton() {
7 const kemeRef = useRef(null);
8 const { user, isAuthenticated } = useAuthStore();
9
10 useEffect(() => {
11 // Create the client once.
12 const keme = Keme.init({
13 gameId: process.env.NEXT_PUBLIC_KEME_GAME_ID!,
14 appName: 'Your Game Support',
15 });
16 kemeRef.current = keme;
17
18 // Log the player in (auto-provisions on first contact).
19 if (isAuthenticated && user) {
20 keme.login({ userId: user.id, username: user.displayName, email: user.email });
21 }
22 }, [isAuthenticated, user]);
23
24 return (
25
26 Get support
27
28 );
29}

Creating Tickets Programmatically

You can create tickets without opening the widget — useful for triggering support flows from in-game error states, purchase failures, or automated crash reports.

typescript
1// Create a ticket without opening the widget — useful for surfacing
2// support from in-game error states, purchase failures, or crash reports.
3const ticket = await keme.tickets.create({
4 subject: 'Purchase failed', // 5–200 chars
5 description: 'I was charged but did not receive my gems.', // 10–5000 chars
6 category: 'billing', // optional
7 priority: 'P2', // optional
8 // gameId is optional here — defaults to the gameId set in Keme.init()
9});
10
11console.log('Created ticket:', ticket.id);

Valid categories

    bug — crashes, glitches, and unexpected behaviourbilling — payments, refunds, and purchase issuesaccount — login problems, account recovery, bansgameplay — progression, content, and balance questionsother — catch-all for anything else

Valid priority values

    P1 — critical / urgent (account access blocked, repeated payment failures)P2 — high (significant issues impacting gameplay or purchases)P3 — normal (standard support requests)P4 — low (general questions, feedback)

Working with Tickets

The keme.tickets namespace covers the full ticket lifecycle for the signed-in player.

typescript
1// List the signed-in player's tickets (optionally paginate/filter).
2const tickets = await keme.tickets.list({ page: 1, limit: 20, status: 'open' });
3
4// Fetch a single ticket with its full message thread.
5const ticket = await keme.tickets.get(ticketId);
6console.log(ticket.messages);
7
8// Reply to a ticket (1–5000 chars).
9await keme.tickets.reply(ticketId, 'Any update on my refund?');
10
11// Rate a resolved ticket (score 1–5, optional comment).
12await keme.tickets.rate(ticketId, 5, 'Fast and friendly, thanks!');

Knowledge Base

Surface self-serve help before a player files a ticket using the keme.kb namespace.

typescript
1// Search the knowledge base.
2const results = await keme.kb.search('how do I restore a purchase');
3
4// Browse categories and featured articles.
5const categories = await keme.kb.categories();
6const featured = await keme.kb.featured();
7
8// Fetch a single article by slug.
9const article = await keme.kb.article('restore-purchases');
10console.log(article.title, article.body);

Error Handling

Every non-2xx response throws a typed KemeError with message, status, and the raw body. Calling an authenticated method before login() throws a KemeError with status 401.

typescript
1import { Keme, KemeError } from '@keme/sdk';
2
3try {
4 await keme.tickets.create({ subject: 'Hi', description: 'Too short' });
5} catch (err) {
6 if (err instanceof KemeError) {
7 console.error('Keme API error', err.status, err.message);
8 // err.body holds the raw error payload from the platform.
9 } else {
10 throw err;
11 }
12}

Complete Integration Example

The example below shows a Next.js 14 App Router integration. It creates the client once, keeps Keme login in sync with your own auth state, and opens the drop-in widget from your own button.

components/KemeSupport.tsx
1// Next.js 14 App Router — bootstrap Keme once, log the player in on auth change.
2'use client';
3
4import { useEffect, useRef } from 'react';
5import { Keme, type KemeClient } from '@keme/sdk';
6import { useAuthStore } from '@/store/auth.store';
7
8export function KemeSupport() {
9 const kemeRef = useRef(null);
10 const { user, isAuthenticated } = useAuthStore();
11
12 // Create the client once.
13 useEffect(() => {
14 kemeRef.current = Keme.init({
15 gameId: process.env.NEXT_PUBLIC_KEME_GAME_ID!,
16 appName: 'Your Game Support',
17 });
18 }, []);
19
20 // Keep Keme auth in sync with your own auth state.
21 useEffect(() => {
22 const keme = kemeRef.current;
23 if (!keme) return;
24
25 if (isAuthenticated && user) {
26 keme.login({
27 userId: user.id, // maps to the portal gameUid
28 username: user.displayName,
29 email: user.email,
30 });
31 } else if (keme.isLoggedIn()) {
32 keme.logout();
33 }
34 }, [isAuthenticated, user]);
35
36 return (
37
38 Get support
39
40 );
41}

Environment variables

.env.local
1# .env.local
2NEXT_PUBLIC_KEME_GAME_ID=your-game-id
Note:The JS SDK is the reference implementation for the Keme SDK contract. Source and issues live in the kemegames-studio/keme-sdk monorepo (JS package under /js). Prefer raw HTTP? See the REST API reference.
Last updated: July 18, 2026Edit this page