Skip to main content

XMDT Server Integration

Your server makes exactly one call. It never carries or receives a player's name, date of birth, phone number, or OTP code.

Dev environment only

Everything on this page describes the dev environment (api-xmdt.dev.alogame.vn). Production has not been provisioned yet — do not point a live build at it.

CallWhenWhy
POST /xmdt/sessionWhenever your client needs to open the XMDT screen, and again at any point you need to trust whether a player has verifiedMints a short-lived token for the SDK — your HMAC secret never reaches a device. Its response also carries xmdtCompleted, read straight from Alogame, never relayed through the player's device — that's your authoritative answer.

Base URL (dev): https://api-xmdt.dev.alogame.vn

Getting a secret

Contact your Alogame Operations contact to enable XMDT for your gameId. You'll be given a secret_key once, at the moment it's generated — it cannot be retrieved again, so store it immediately in your own secrets management, not in a config file that ends up in version control.

Signing requests

This call uses the following signature scheme, and it's simpler than Alogame's payment contracts: no timestamp, no replay window, no field sorting.

X-Signature: HMAC-SHA256(secret_key, raw_request_body), lowercase hex

The signed message is the raw JSON body bytes exactly as sent — not a re-serialized or sorted version of it.

Sign the raw body, never a re-serialized object

If your framework parses the JSON and you sign the re-serialized form, key order, spacing, and number formatting can all differ from what you actually send, and the signature will not match. Capture the raw body before your JSON parser consumes it (see the payment API authentication guide for language-specific examples of doing this correctly — same principle applies here even though the signing formula itself is different).

Node.js
const crypto = require("crypto");

function signXmdtBody(rawBody, secret) {
return crypto.createHmac("sha256", secret).update(rawBody).digest("hex");
}
Python
import hmac, hashlib

def sign_xmdt_body(raw_body: bytes, secret: str) -> str:
return hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
PHP
function signXmdtBody(string $rawBody, string $secret): string {
return hash_hmac('sha256', $rawBody, $secret);
}

Send it as the X-Signature header, Content-Type: application/json, on the call below. There is no separate service-id header to set — gameId in the request body is what's verified against.

POST /xmdt/session — mint a session token

Call this whenever your client is about to open the XMDT screen (AlogameKycSdk.show()).

Request

Request body
{
"gameId": "your_game_id",
"username": "player_account_name",
"serverId": "server_01",
"roleId": "character_123"
}
FieldRequiredNotes
gameIdYesYour registered game code
usernameYesYour own stable per-player identifier — this, not a device ID, is what ties a player to their XMDT record
serverIdNoOmit if your game has no server selection at this point
roleIdNoOmit if the player hasn't picked a character yet
curl
BODY='{"gameId":"your_game_id","username":"player_account_name"}'
SIG=$(printf '%s' "$BODY" | openssl dgst -sha256 -hmac "$SECRET" | sed 's/^.* //')

curl -X POST https://api-xmdt.dev.alogame.vn/xmdt/session \
-H "Content-Type: application/json" \
-H "X-Signature: $SIG" \
-d "$BODY"

Response

200 OK
{
"sessionToken": "opaque-token-hand-this-to-your-client",
"expiresAt": "2026-08-07T12:15:00Z",
"xmdtRequired": true,
"xmdtCompleted": false
}
FieldMeaning
sessionTokenPass this straight to your client — it's what AlogameKycSdk.setGameRole() needs. Valid for 15 minutes.
xmdtRequiredWhether the SDK's form shows a close button. Set per-game by Alogame Operations; you don't configure this per-call.
xmdtCompletedThis player already has a complete XMDT record — your client can skip calling show() entirely if you want to avoid a no-op screen open.

The token is single-use for the collect step and expires in 15 minutes — mint a fresh one for each show() call rather than caching and reusing one across sessions.

Using this as the authoritative check

Do not gate anything on the SDK's onResult callback alone — that comes from the player's device, and a modified client can report success without ever verifying anything. At the point you actually need to trust the answer (letting a player into a match, for example), call this same endpoint again and read xmdtCompleted off the response. There's no separate status route: the field is authoritative because it comes straight from Alogame over the same signed, server-to-server call — never relayed through the player's device.

Cache a true result on your side rather than minting again on every login — a completed record doesn't become incomplete again, and minting is rate-limited per player (see Errors below).

Errors

HTTPcodeMeaning
400invalid_requestMalformed JSON, or gameId/username missing
401missing_headersNo X-Signature header sent
401invalid_signatureSignature doesn't match — check you signed the exact raw body
403unknown_servicegameId is not registered or is inactive
409xmdt_disabledXMDT is not (yet) enabled for your gameId — contact Operations
409email_required_unsupportedYour game has an email-verification requirement configured that this SDK flow doesn't support — contact Operations
429rate_limitedToo many mint requests for this player/IP in a short window — back off and retry, no detail on which limit was hit
500internal_errorRetry with backoff

All error responses share the shape {"code": "...", "message": "..."}.

Next steps