GAGA Game Server API (gmtool) (0.2)

Download OpenAPI specification:openapi.json

Server-to-server API that the game server (X-Legend) implements and the GAGA backend calls.

Rev 0.2 (2026-10-07) — rebuilt on top of X-Legend's existing /gmtool/* interface (ELI, Discord 2026-10-06). Paths and field names are X-Legend's. Items marked 🔶 GAGA suggestion are additions/clarifications we ask X-Legend to confirm or counter-propose.

中文摘要:本版直接採用 X-Legend 提供的 /gmtool/* 路徑與欄位。 標示 🔶 GAGA suggestion 的部分是我們建議補充或需要確認的項目,請貴方確認或提出替代方案。 本 API 為伺服器對伺服器(GAGA 後端 → 遊戲伺服器),與 GAGA SDK、遊戲客戶端、Google/Apple 內購無關。

Summary of GAGA suggestions

# Endpoint Suggestion Why
S1 /gmtool/role/query Add type=xluserid (+ sid) → returns the character list of that GAGA ID on that server Web Shop / Funtap know only the GAGA ID. Player must pick a character. Current query goes the other way (aid/cid → xluserid).
S2 All Define md5str formula + add time (replay window 300 s) Formula not specified yet. Proposal below — if you already have a formula, send it and we adopt yours.
S3 /gmtool/role/orders orderid idempotency rules (duplicate = success, no second delivery) GAGA retries on timeout; must never double-deliver.
S4 /gmtool/role/orders Add items (same format as sendmail); productid = audit only GAGA CMS stays the single source of package content → no 3rd place to configure packages (GAGA CMS + Funtap already).
S5 new /gmtool/role/orders/query Look up an order by orderid Recovery after timeout / reconciliation. Without it GAGA cannot know if a timed-out order was delivered.
S6 /gmtool/role/sendmail Use xluserid (not uid) + optional refid for dedupe Consistent naming; pre-register launch-day batch rewards use sendmail and also need retry safety.
S7 All Common response {status, msg, ...} + error code table Only status:"1" is defined today. GAGA needs to know which errors to retry.
S8 Ops Staging + production base URLs, HTTPS, IP allowlist Funtap requires separate test/live endpoints. gmtool is an admin interface — expose only these endpoints to GAGA.

Where this API is used

Flow Calls
Funtap (VN) top-up — player pays on nap.funtap.games Funtap → GAGA BE → server/list, role/query → (payment) → role/orders
GAGA Web Shop (TH / ROW) same: server/list → role/query → (payment) → role/orders
Item Code / gift code redeem on GAGA web role/giftcode
Pre-register rewards, ops compensation role/sendmail

Identity

xluserid = GAGA ID, canonical form without dashes, 16 chars, e.g. VNGA284190375216 (display form VN-GA-284190375216). It is the same value the game receives as gaga_id from GAGA SDK login. 🔶 Please confirm the game stores this value as xluserid unchanged.

Transport

🔶 S8

  • Method: POST, body application/x-www-form-urlencoded, response application/json, UTF-8. If your gmtool already uses GET + query string, keep it and tell us — the parameters stay the same.
  • HTTPS only (TLS 1.2+).
  • IP allowlist: accept only GAGA backend egress IPs (GAGA sends the list per environment). gmtool normally has full GM power; please expose only the endpoints in this document to GAGA.
  • Timeout on GAGA side: 10 s.
Env Base URL (provided by X-Legend) Secret (provided by X-Legend or GAGA, one per env)
Staging https://<stg-host> <stg secret>
Production https://<prod-host> <prod secret> — never shared with staging

Signature

🔶 S2 — md5str rule.

Every request carries time and md5str.

  1. Take all request parameters except md5str.
  2. Sort by parameter name, ASCII ascending.
  3. Join as name=value with &. Use raw values (not URL-encoded). Include empty values as name=.
  4. Append &key=<SECRET>.
  5. md5str = MD5 of the UTF-8 string, lowercase hex (32 chars).

Reject if |now − time| > 300 seconds (status -2). Compare md5str in constant time.

Worked example (secret = test_secret_123), /gmtool/role/orders:

cid=2356423&currency=VND&items=1:900001:1100&orderid=GW20261007A1B2C3D4&payment=funtap&price=250000&productid=GCOIN_1000&sid=1&time=1791360000&xluserid=VNGA284190375216&key=test_secret_123
→ md5str = 6a2352e8c0632b43fd45ef28213680a0

/gmtool/server/list with only time=1791360000 → time=1791360000&key=test_secret_123 → 55a7ec1d94c646c71fd63a962474a2b2

Response format

🔶 S7

HTTP is always 200 when the request reached the game logic; the result is in status.

{ "status": "1", "msg": "OK", "...": "endpoint data" }
status Meaning GAGA retries?
1 Success (also for a duplicate orderid / refid, see Idempotency) —
-1 Invalid md5str No — alert
-2 time outside ±300 s Yes (once, after clock check)
-3 Missing / invalid parameter No
-4 Server (sid) not found No
-5 Character not found / not owned by xluserid No
-6 Unknown item / invalid amount / mailbox cannot receive No — alert
-7 orderid / refid already used with different content No — alert
-8 Gift code invalid No
-9 Gift code already used / limit reached No
-10 Gift code expired No
-98 Server under maintenance Yes
-99 Internal error Yes

HTTP 5xx or timeout → GAGA treats as -99 (retry). If you already have your own codes, send the list and we map them.

Idempotency

🔶 S3, S5, S6

  • orderid (GAGA transaction id) is unique per purchase. Store it with a unique constraint in the same DB transaction that creates the delivery.
  • Same orderid + same content again → do not deliver again; return status:"1", duplicate:true.
  • Same orderid + different xluserid / sid / cid / items → status:"-7".
  • GAGA retry: on timeout / -98 / -99 GAGA first calls /gmtool/role/orders/query. found:true → done. found:false → resend /orders with the same orderid. Back-off 30 s, 2 min, 10 min, 1 h, then every 6 h up to 72 h, then manual.
  • Keep orderid records ≥ 90 days. Same rules apply to refid on sendmail.

Items format

items = <Type>:<ID>:<Amount>, multiple joined with , — e.g. 1:900001:1100,2:500123:1. 🔶 Please send: the list of Type values, the G Coin Type:ID, max items per mail, and mailbox expiry rule.

What X-Legend provides

  1. Staging + production base URLs, and the secret (or we generate it).
  2. G Coin Type:ID, other item IDs GAGA may send, Type list.
  3. Confirmation / counter-proposal for S1–S8.
  4. A staging server + test accounts with characters for joint testing with Funtap.

Not in scope

GAGA SDK, game client UI, Google Play / App Store IAP, refunds / item removal (agree separately).

Server & Character

Lookups while the player picks server + character on Web Shop / Funtap.

(1) Get server list

X-Legend endpoint, unchanged. GAGA caches the result for 5 minutes.

🔶 GAGA suggestion: add optional status (open / maintenance) so the Web Shop can grey out a server.

Request Body schema: application/x-www-form-urlencoded
required
time
required
string

🔶 Unix seconds. Rejected if more than 300 s from server time.

md5str
required
string^[0-9a-f]{32}$

See Signature.

Responses

Request samples

Content type
application/x-www-form-urlencoded
time=1791360000&md5str=55a7ec1d94c646c71fd63a962474a2b2

Response samples

Content type
application/json
{
  • "status": "1",
  • "msg": "OK",
  • "servers": [
    ]
}

(2) Query character

X-Legend endpoint. Existing types aid / cid → returns xluserid (kept as is).

🔶 GAGA suggestion S1 — required for Web Shop / Funtap: add type=xluserid with data = GAGA ID and sid. Returns all characters of that GAGA ID on that server (array, player picks one). No character on that server → status:"-5".

Request Body schema: application/x-www-form-urlencoded
required
time
required
string

🔶 Unix seconds. Rejected if more than 300 s from server time.

md5str
required
string^[0-9a-f]{32}$

See Signature.

type
required
string
Enum: "aid" "cid" "xluserid"

aid / cid = existing. xluserid = 🔶 new (GAGA ID).

data
required
string

The ID matching type.

sid
string

Server id. 🔶 Required when type=xluserid.

Responses

Request samples

Content type
application/x-www-form-urlencoded
Example
type=xluserid&data=VNGA284190375216&sid=1&time=1791360000&md5str=8b15a7185de7c817ccc46910f2ba36c9

Response samples

Content type
application/json
Example
{
  • "status": "1",
  • "msg": "OK",
  • "xluserid": "VNGA284190375216",
  • "roles": [
    ]
}

Delivery

Deliver purchased items, check delivery, send reward mail.

(4) Top-up order fulfillment

X-Legend endpoint. Used for every paid delivery (Web Shop and Funtap), after GAGA has verified the payment. Return status:"1" only after the delivery is committed.

🔶 S3 — idempotent on orderid (see Idempotency).

🔶 S4 — GAGA sends items (what to deliver). productid is for your reports only. If the game must deliver by productid, please send the product catalog (productid → items) and we will use your IDs in GAGA CMS instead.

Delivery to mailbox vs direct to wallet: your choice — please tell us which (affects the orders/query status).

Request Body schema: application/x-www-form-urlencoded
required
time
required
string

🔶 Unix seconds. Rejected if more than 300 s from server time.

md5str
required
string^[0-9a-f]{32}$

See Signature.

xluserid
required
string

GAGA ID

sid
required
string
cid
required
string
orderid
required
string <= 64 characters

GAGA transaction id. Idempotency key.

productid
required
string

GAGA package id. 🔶 Audit only (S4).

items
required
string (Items) ^\d+:\d+:\d+(,\d+:\d+:\d+)*$

<Type>:<ID>:<Amount> joined with ,.

price
required
string

Amount paid, decimal string in major units (VND has no decimals; THB/USD up to 2).

currency
required
string
Enum: "VND" "THB" "USD"

ISO 4217

payment
required
string
Enum: "funtap" "chillpay" "razer"

Sales channel.

Responses

Request samples

Content type
application/x-www-form-urlencoded
xluserid=VNGA284190375216&sid=1&cid=2356423&orderid=GW20261007A1B2C3D4&productid=GCOIN_1000&items=1%3A900001%3A1100&price=250000&currency=VND&payment=funtap&time=1791360000&md5str=6a2352e8c0632b43fd45ef28213680a0

Response samples

Content type
application/json
Example
{
  • "status": "1",
  • "msg": "OK",
  • "orderid": "GW20261007A1B2C3D4",
  • "duplicate": false,
  • "delivered_at": 1791360003
}

(6) 🔶 Query order — new

🔶 GAGA suggestion S5 — new endpoint. Returns the delivery for an orderid. GAGA calls it after a timeout / -98 / -99 on /orders, and in daily reconciliation. found:false → GAGA resends /orders with the same orderid.

Request Body schema: application/x-www-form-urlencoded
required
time
required
string

🔶 Unix seconds. Rejected if more than 300 s from server time.

md5str
required
string^[0-9a-f]{32}$

See Signature.

orderid
required
string <= 64 characters

Responses

Request samples

Content type
application/x-www-form-urlencoded
orderid=GW20261007A1B2C3D4&time=1791360000&md5str=bf1118a9437c2bdfc60a797c8f01759f

Response samples

Content type
application/json
Example
{
  • "status": "1",
  • "msg": "OK",
  • "found": true,
  • "order": {
    }
}

(3) Send mail / items

X-Legend endpoint. For non-purchase rewards: pre-register rewards (launch-day batch), event rewards, compensation. Paid orders go through /gmtool/role/orders, not this endpoint.

🔶 S6 — use xluserid instead of uid (same as other endpoints; tell us if uid is a different ID). Add optional refid: if present, apply the same idempotency rules as orderid (launch-day batch will be retried on failure).

Request Body schema: application/x-www-form-urlencoded
required
time
required
string

🔶 Unix seconds. Rejected if more than 300 s from server time.

md5str
required
string^[0-9a-f]{32}$

See Signature.

xluserid
required
string

GAGA ID. (Original spec field name uid — 🔶 please confirm.)

sid
required
string
cid
required
string
title
required
string <= 50 characters
body
required
string <= 500 characters
items
required
string (Items) ^\d+:\d+:\d+(,\d+:\d+:\d+)*$

<Type>:<ID>:<Amount> joined with ,.

refid
string <= 64 characters

🔶 Optional idempotency key, e.g. PREREG-2026-VN-000123.

Responses

Request samples

Content type
application/x-www-form-urlencoded
xluserid=VNGA284190375216&sid=1&cid=2356423&title=Pre-register%20reward&body=Thank%20you%20for%20pre-registering%21&items=2%3A500123%3A1%2C2%3A500124%3A5&refid=PREREG-2026-VN-000123&time=1791360000&md5str=5f24801d3214e88f7f09818faed5668c

Response samples

Content type
application/json
{
  • "status": "1",
  • "msg": "OK",
  • "mailid": "M-88123901",
  • "duplicate": false
}

Gift Code

Redeem promotional / Item Code serials.

(5) Redeem gift / promo code

X-Legend endpoint. Used when a player redeems a code on the GAGA website (Item Code / marketing serials).

🔶 GAGA suggestion: return the granted items, and use distinct statuses for invalid / used / expired (-8 / -9 / -10) so the website can show the right message. Please confirm who generates the codes (game or GAGA) and whether one code is per-account or per-character.

Request Body schema: application/x-www-form-urlencoded
required
time
required
string

🔶 Unix seconds. Rejected if more than 300 s from server time.

md5str
required
string^[0-9a-f]{32}$

See Signature.

xluserid
required
string
sid
required
string
cid
required
string
code
required
string <= 64 characters

Responses

Request samples

Content type
application/x-www-form-urlencoded
xluserid=VNGA284190375216&sid=1&cid=2356423&code=GAGA-OBT-7K2M9Q&time=1791360000&md5str=bf4f43677753035b4de5d36654fc4d1d

Response samples

Content type
application/json
Example
{
  • "status": "1",
  • "msg": "OK",
  • "items": "2:500123:1"
}