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)
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.
1# npm2npm install @keme/sdk34# yarn5yarn add @keme/sdk67# pnpm8pnpm 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.
1import { Keme } from '@keme/sdk';23// 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 header7});
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.
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 provisioned6 email: player.email, // optional7});89// Later, when the player signs out of your game:10keme.logout();1112// 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)
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.
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.
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';56export function SupportButton() {7 const kemeRef = useRef(null); 8 const { user, isAuthenticated } = useAuthStore();910 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;1718 // 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]);2324 return (2526 Get support2728 );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.
1// Create a ticket without opening the widget — useful for surfacing2// support from in-game error states, purchase failures, or crash reports.3const ticket = await keme.tickets.create({4 subject: 'Purchase failed', // 5–200 chars5 description: 'I was charged but did not receive my gems.', // 10–5000 chars6 category: 'billing', // optional7 priority: 'P2', // optional8 // gameId is optional here — defaults to the gameId set in Keme.init()9});1011console.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.
1// List the signed-in player's tickets (optionally paginate/filter).2const tickets = await keme.tickets.list({ page: 1, limit: 20, status: 'open' });34// Fetch a single ticket with its full message thread.5const ticket = await keme.tickets.get(ticketId);6console.log(ticket.messages);78// Reply to a ticket (1–5000 chars).9await keme.tickets.reply(ticketId, 'Any update on my refund?');1011// 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.
1// Search the knowledge base.2const results = await keme.kb.search('how do I restore a purchase');34// Browse categories and featured articles.5const categories = await keme.kb.categories();6const featured = await keme.kb.featured();78// 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.
1import { Keme, KemeError } from '@keme/sdk';23try {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.
1// Next.js 14 App Router — bootstrap Keme once, log the player in on auth change.2'use client';34import { useEffect, useRef } from 'react';5import { Keme, type KemeClient } from '@keme/sdk';6import { useAuthStore } from '@/store/auth.store';78export function KemeSupport() {9 const kemeRef = useRef(null); 10 const { user, isAuthenticated } = useAuthStore();1112 // 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 }, []);1920 // Keep Keme auth in sync with your own auth state.21 useEffect(() => {22 const keme = kemeRef.current;23 if (!keme) return;2425 if (isAuthenticated && user) {26 keme.login({27 userId: user.id, // maps to the portal gameUid28 username: user.displayName,29 email: user.email,30 });31 } else if (keme.isLoggedIn()) {32 keme.logout();33 }34 }, [isAuthenticated, user]);3536 return (3738 Get support3940 );41}
Environment variables
1# .env.local2NEXT_PUBLIC_KEME_GAME_ID=your-game-id
/js). Prefer raw HTTP? See the REST API reference.