REST API Integration v1 HTTP

Overview

Every Keme SDK (JavaScript, Unity, Android, iOS) wraps the same player-portal REST API. If you can't use a native SDK — or you want full control — you can call the API directly. This is the exact contract the SDKs mirror; the JavaScript SDK is the reference implementation.

The API is player-facing: each call is authenticated with a player bearer token obtained from login. There is no separate registration — the first login auto-provisions the player. All endpoints return JSON wrapped in a consistent envelope.

  • Player login and profile (auto-provisioning on first contact)
  • Full ticket lifecycle — create, list, get, reply, rate
  • Knowledge base — search, categories, featured, article-by-slug
  • Consistent { success, data } response envelope
  • Typed error responses with standard HTTP status codes

Base URL

All requests are made to the following base URL. Append the endpoint path to it for every request.

bash
1https://api.kemegames.com/api/v1

Response Envelope

Every successful response is wrapped in a { success, data } envelope — the payload you want is in data. List endpoints nest a paginated object inside data. The SDKs unwrap this for you and normalise lists to a plain array.

Single resource
1{
2 "success": true,
3 "data": {
4 "id": "tkt_xxxxxxxxxxxxxxxxxx",
5 "subject": "Payment did not go through",
6 "status": "open"
7 }
8}
List (paginated)
1{
2 "success": true,
3 "data": {
4 "data": [ /* Ticket[] */ ],
5 "total": 42,
6 "page": 1,
7 "limit": 20
8 }
9}

Authentication

Portal endpoints use a player bearer token — a JWT with a 7-day expiry returned by POST /portal/auth/login. Send it in the Authorization header using the Bearer scheme on every authenticated request. All requests use Content-Type: application/json.

bash
1curl https://api.kemegames.com/api/v1/portal/auth/me \
2 -H "Authorization: Bearer <player-token>" \
3 -H "Content-Type: application/json"
Note:The player-portal API allows cross-origin calls from any origin, so browser and WebGL builds can call it directly. Auth uses a bearer token — there are no cookies.

Concepts: gameId vs userId

  • gameId identifies your game/app (e.g. game-marble-sort). It is set once at SDK init and passed when creating a ticket.
  • userId identifies a player uniquely within your game and maps to the portal gameUid. The first login auto-provisions the player — no separate registration.
  • A banned player's login is rejected with 403.

Errors

Non-2xx responses return { message, statusCode }, where message is a string or an array of validation strings. The SDKs surface this as a typed KemeError with message, status, and the raw body.

json
1{
2 "message": [
3 "subject must be longer than or equal to 5 characters"
4 ],
5 "statusCode": 400
6}

The API uses standard HTTP status codes:

  • 200 OK — Request succeeded. The result is in data.
  • 201 Created — Resource created (e.g. a new ticket). The new resource is in data.
  • 400 Bad Request — The body or query parameters failed validation.
  • 401 Unauthorized — The Authorization header is missing or the token is invalid or expired.
  • 403 Forbidden — The player is not permitted to perform the action (e.g. a banned player).
  • 404 Not Found — The requested resource does not exist.
  • 500 Internal Server Error — An unexpected server-side error occurred.

Login

Authenticate a player. Send your player's id as gameUid; the first login for a given gameUid auto-provisions the player. username and email are optional and used only when the player is first provisioned. Returns a token and the player object.

bash
1curl -X POST https://api.kemegames.com/api/v1/portal/auth/login \
2 -H "Content-Type: application/json" \
3 -d '{
4 "gameUid": "player-12345",
5 "username": "Ari",
6 "email": "ari@example.com"
7 }'
Response 200
1{
2 "success": true,
3 "data": {
4 "token": "",
5 "player": {
6 "id": "plr_xxxxxxxxxxxx",
7 "username": "Ari",
8 "gameUid": "player-12345",
9 "vipTier": "bronze"
10 }
11 }
12}

Current Player

Fetch the authenticated player's profile.

bash
1curl https://api.kemegames.com/api/v1/portal/auth/me \
2 -H "Authorization: Bearer <player-token>"

Creating a Ticket

Submit a new ticket for the authenticated player. subject (5–200 chars), description (10–5000 chars), and gameId are required. category and priority are optional. Returns 201 Created with the new ticket.

bash
1curl -X POST https://api.kemegames.com/api/v1/portal/tickets \
2 -H "Authorization: Bearer <player-token>" \
3 -H "Content-Type: application/json" \
4 -d '{
5 "subject": "Payment did not go through",
6 "description": "I was charged but did not receive my gems.",
7 "gameId": "game-marble-sort",
8 "category": "billing",
9 "priority": "P2"
10 }'

Valid categories: bug | billing | account | gameplay | other. Valid priorities: P1 (critical) | P2 (high) | P3 (normal) | P4 (low).

Response 201
1{
2 "success": true,
3 "data": {
4 "id": "tkt_xxxxxxxxxxxxxxxxxx",
5 "subject": "Payment did not go through",
6 "status": "open",
7 "priority": "P2",
8 "category": "billing",
9 "createdAt": "2026-07-18T10: 23: 45Z",
10 "updatedAt": "2026-07-18T10: 23: 45Z"
11 }
12}

Listing Tickets

Retrieve the authenticated player's tickets. Optional query parameters: page, limit, and status. The paginated list is nested inside data.

bash
1curl "https://api.kemegames.com/api/v1/portal/tickets?page=1&limit=20&status=open" \
2 -H "Authorization: Bearer <player-token>"

Getting a Ticket

Fetch a single ticket by id, including its messages[] thread.

bash
1curl https://api.kemegames.com/api/v1/portal/tickets/tkt_xxxx \
2 -H "Authorization: Bearer <player-token>"

Replying to a Ticket

Add a message to a ticket. The body field (1–5000 chars) is required. Returns the new message.

bash
1curl -X POST https://api.kemegames.com/api/v1/portal/tickets/tkt_xxxx/messages \
2 -H "Authorization: Bearer <player-token>" \
3 -H "Content-Type: application/json" \
4 -d '{ "body": "Any update on my refund?" }'

Rating a Ticket

Rate a resolved ticket. score (1–5) is required; comment is optional.

bash
1curl -X POST https://api.kemegames.com/api/v1/portal/tickets/tkt_xxxx/rate \
2 -H "Authorization: Bearer <player-token>" \
3 -H "Content-Type: application/json" \
4 -d '{ "score": 5, "comment": "Fast and friendly, thanks!" }'

Knowledge Base

Surface self-serve help before a player files a ticket. The knowledge base endpoints are also player-authenticated.

  • GET /portal/kb/search?q=<query> — search articles
  • GET /portal/kb/categories — list categories
  • GET /portal/kb/featured — featured articles
  • GET /portal/kb/articles/:slug — a single article by slug
bash
1curl "https://api.kemegames.com/api/v1/portal/kb/search?q=restore%20purchase" \
2 -H "Authorization: Bearer <player-token>"

Language Examples

The following examples show the create-ticket call from the Creating a Ticket section in JavaScript/Node.js and Python. In production, prefer the JavaScript SDK, which handles the envelope, token persistence, and errors for you.

JavaScript / Node.js

Create ticket — Node.js (fetch)
1const response = await fetch('https://api.kemegames.com/api/v1/portal/tickets', {
2 method: 'POST',
3 headers: {
4 'Authorization': `Bearer ${playerToken}`,
5 'Content-Type': 'application/json',
6 },
7 body: JSON.stringify({
8 subject: 'Payment did not go through',
9 description: 'I was charged but did not receive my gems.',
10 gameId: 'game-marble-sort',
11 category: 'billing',
12 priority: 'P2',
13 }),
14});
15
16if (!response.ok) {
17 const err = await response.json();
18 const message = Array.isArray(err.message) ? err.message.join(', ') : err.message;
19 throw new Error(`Keme API error ${response.status}: ${message}`);
20}
21
22const { data: ticket } = await response.json(); // unwrap the envelope
23console.log('Created ticket:', ticket.id);

Python

Create ticket — Python (requests)
1import requests
2
3response = requests.post(
4 "https://api.kemegames.com/api/v1/portal/tickets",
5 headers={
6 "Authorization": f"Bearer {player_token}",
7 "Content-Type": "application/json",
8 },
9 json={
10 "subject": "Payment did not go through",
11 "description": "I was charged but did not receive my gems.",
12 "gameId": "game-marble-sort",
13 "category": "billing",
14 "priority": "P2",
15 },
16)
17
18response.raise_for_status()
19ticket = response.json()["data"] # unwrap the envelope
20print("Created ticket:", ticket["id"])
Last updated: July 18, 2026Edit this page