개발자로 돌아가기

< REST 통합 />

UTD Games

앱에 완전한 게임 카탈로그를 추가하세요 — UTD가 여러분의 에이전시로서 운영합니다.

버전 1.0비공개 베타
REST APIServer-to-serverAny language
games_integration.sh
# From your backend, read the launch flag with the API token.
# Show or hide games in your app based on the response.
curl https://utdsoftware.com/api/games/launch-flag \
  -H "Authorization: Bearer 12|xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

# => { "games_enabled": true }

# games_enabled reflects the games wallet balance (gated at zero).
# Cache briefly server-side; never ship this token to a client app.

REST

프로토콜

launch-flag

런타임 플래그

Server-to-server

통합

Agency

모델

< utd-games />

주요 기능

직접 무언가를 구축하거나 호스팅하지 않아도 앱에서 완전한 게임 카탈로그를 제공할 수 있게 해주는 서버 대 서버 REST 통합입니다. UTD는 여러분의 게임 에이전시 역할을 합니다: 대시보드에서 각 프로젝트를 프로비저닝하면 저희 팀이 검토하고 활성화하며, 백엔드는 단 하나의 launch flag를 읽어 게임을 표시하거나 숨깁니다. 게임플레이는 앱과 공급자 사이에서 직접 실행됩니다. UTD는 프로비저닝, launch flag, 월간 커미션 청구를 처리하며 공급자 시크릿은 결코 앱에 도달하지 않습니다.

서버 대 서버 REST — 어떤 언어에서든 통합 가능, SDK 불필요

하나의 launch flag 엔드포인트가 게임 표시 여부를 결정

범위가 제한된 API 토큰(launch-flag 전용) — 서버에서 안전하게 실행

자동 게이팅: 지갑 잔액이 0이 되면 게임이 비활성화됩니다

에이전시 모델 — UTD가 모든 프로젝트를 출시 전에 프로비저닝하고 검토

순 코인에 대한 월간 커미션 청구, 여러분을 위해 계산 및 정산

공급자 시크릿은 서버 측에 유지되며 앱에 결코 노출되지 않습니다

계정당 최대 10개의 활성 API 토큰, 언제든지 취소 가능

< utd-games />

시작하기

1

설치

Terminal
# Mint a server-to-server API token (scoped to launch-flag only).
# Run once from your dashboard session; the secret is shown a single time.
curl -X POST https://utdsoftware.com/api/games/api-tokens \
  -H "Authorization: Bearer <your-session-token>" \
  -H "Content-Type: application/json" \
  -d '{"name":"production-server"}'

# => { "token": { "id": 12, "name": "production-server", ... },
#      "secret": "12|xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" }  # store securely
2

접근 요청

앱에 완전한 게임 카탈로그를 추가하세요 — UTD가 여러분의 에이전시로서 운영합니다.

< utd-games />

API 레퍼런스

Game callbacks (hosted on YOUR backend)

Our games platform calls these three endpoints on your backend to read and move each player's coin balance. All three are POST, live under one base URL at the /api/leader-cc-game/ prefix (base = the Callback base URL you enter when creating the project — e.g. base https://api.yourapp.com means we call https://api.yourapp.com/api/leader-cc-game/get-user-info), and ALWAYS answer HTTP 200 — success or failure travels in the body as errorCode (0 = success). The response envelope is {errorCode, errorMsg, data} — NOT {code/status/msg}. Any other envelope is rejected as "structure incorrect". Your APP_KEY (the shared signing secret) is shown in the "Callback test" step of the create-project wizard before you submit, and stays the same after activation. Signature rule for every endpoint: concatenate the values in the exact order listed (a missing/absent field enters the string as "", an empty string), append APP_KEY, md5 — then compare case-insensitively.

How a game session works (read this first)field
launchUrl + '&uid=' + playerId + '&token=' + playerToken

1) Your app opens the game's Launch URL in a WebView and appends the player's identity: &uid=<player id>&token=<a session token YOUR backend issued for that player>. 2) The game engine calls your get-user-info callback with that same uid + token — you MUST verify the token really belongs to that uid (mismatch → errorCode 10003); the signature only proves the call came from the games platform, the token is what proves the player. 3) Every bet/win during play calls your change-balance callback. coin values are in YOUR app's coin currency (the coin price you set at submission, e.g. 1 USD = 13,000 coins). In get-user-info, nickname/avatar personalize the game UI; vipLevel and water can be 0 if you don't use them.

POST {base}/api/leader-cc-game/get-user-infomethod
POST {base}/api/leader-cc-game/get-user-info Content-Type: application/json { "gameId", "uid", "token", "roomId", "sign" }

Called once when a player opens a game. Verify sign = md5(gameId + uid + token + roomId + APP_KEY) — concatenated values, no separators, compared case-insensitively — then return the player's profile and current balance. uid and coin are REQUIRED inside data. Bad signature → errorCode 10004. Unknown player or uid/token mismatch → 10003. Player blocked or games intentionally disabled on your side → 30001.

반환값: { errorCode: 0, errorMsg: "success", data: { uid, nickname, avatar, coin, vipLevel, water } }HTTP 200 always. data.coin is the player's current balance (integer).

POST {base}/api/leader-cc-game/change-balancemethod
POST {base}/api/leader-cc-game/change-balance Content-Type: application/json { "orderId", "gameId", "roundId", "uid", "coin", "type", "rewardType", "token", "winId", "roomId", "sign" }

Called on every bet and win. type=1 → deduct coin (bet), type=2 → add coin (win). Verify sign = md5(orderId + gameId + roundId + uid + coin + type + rewardType + token + winId + roomId + APP_KEY). Apply the change, then read the wallet AGAIN and return the NEW balance — returning the old unchanged balance is the #1 activation rejection. Insufficient balance on a bet → errorCode 4004 and move no money. Must be idempotent by orderId: the same orderId arriving twice moves money once and returns the current balance with errorCode 0. coin=0 is a valid no-op (used by the dashboard self-test).

반환값: { errorCode: 0, errorMsg: "success", data: { coins: <NEW balance>, coin: <NEW balance> } }HTTP 200 always. data.coins MUST reflect the balance AFTER the debit/credit.

POST {base}/api/leader-cc-game/make-up-ordersmethod
POST {base}/api/leader-cc-game/make-up-orders Content-Type: application/json { "orderId", "gameId", "roundId", "uid", "coin", "rewardType", "winId", "roomId", "sign" }

Re-settles a reward the platform believes was lost (rare). Signature has NO type and NO token: sign = md5(orderId + gameId + roundId + uid + coin + rewardType + winId + roomId + APP_KEY). CRITICAL: share ONE idempotency key with change-balance, scoped to the orderId. If that orderId was already settled, return the current balance with errorCode 0 and move NO money — a separate per-endpoint key double-credits (mints coins).

반환값: { errorCode: 0, errorMsg: "success", data: { coin: <NEW balance> } }HTTP 200 always. Returns the balance after the credit (or the current balance on an idempotent replay).

Error codesfield
{ "errorCode": 30001, "errorMsg": "games disabled", "data": [] }

0 success · 4004 insufficient coins on a bet · 4005 missing/invalid parameter or a REAL backend failure ONLY (platform monitoring alerts on 4005 — never use it for intentional stops) · 10003 user not found / uid-token mismatch · 10004 signature verification failed · 30001 intentional block (player banned, or games disabled on your side). Every response is HTTP 200 with the {errorCode, errorMsg, data} envelope; error responses use data: [].

Launch flag

The one endpoint your app calls at runtime. Read it server-to-server with a scoped API token to decide whether to show or hide games. Reflects the games wallet balance — automatically gated at zero.

GET /api/games/launch-flagmethod
GET /api/games/launch-flag Authorization: Bearer <api-token>

Returns whether games are enabled for the account. Requires an API token carrying the games:launch-flag ability (a session token also works for owner testing). Cache the result briefly server-side; never expose the token to a client app.

반환값: { games_enabled: boolean }games_enabled is true when the games wallet has a positive balance, false when gated.

API tokens

Server-to-server tokens scoped to launch-flag only — they cannot read analytics, mint other tokens, or touch any other route. Managed from your dashboard session (the session bearer). Up to 10 active tokens per account.

POST /api/games/api-tokensmethod
POST /api/games/api-tokens Authorization: Bearer <session-token> Content-Type: application/json

Mints a new API token scoped to the games:launch-flag ability. The plaintext secret is returned once and never again — store it securely. Fails with 422 token_limit_reached at 10 active tokens.

매개변수

  • namestring필수

    A label to identify the token (max 60 chars), e.g. "production-server".

반환값: { token: { id, name, last_used_at, created_at }, secret: string }secret is the plaintext bearer, shown only in this response (HTTP 201).

GET /api/games/api-tokensmethod
GET /api/games/api-tokens Authorization: Bearer <session-token>

Lists your active games API tokens (masked — no secrets). Only games tokens are listed; your browser session token is never included.

반환값: { tokens: Array<{ id, name, last_used_at, created_at }> }Metadata only; the secret is never re-exposed after creation.

DELETE /api/games/api-tokens/{id}method
DELETE /api/games/api-tokens/{id} Authorization: Bearer <session-token>

Revokes (permanently deletes) one of your games API tokens by id. Returns 404 if the id is not one of your games tokens.

반환값: { ok: true }Revocation is immediate and irreversible.

Provisioning (agency model)

UTD is your games agency: each project is created from the dashboard and reviewed by our team before it goes live. You do not integrate the game engine yourself — gameplay runs directly between your app and the provider; UTD handles provisioning, the launch flag, and monthly billing. Submission order: (1) get your signing key from GET /api/games/draft-credentials, (2) build the three callbacks, (3) pass POST /api/games/draft-callback-test with your callback URL + test account, (4) submit the project with the SAME values — submissions without a passing test are rejected with callback_test_required. The dashboard wizard walks these steps for you.

GET /api/games/draft-credentialsmethod
GET /api/games/draft-credentials Authorization: Bearer <session-token>

Returns your signing key (app_key) BEFORE any project exists, so you can implement signature verification and test it. The key is stable per account — calling twice returns the same key, and the project you later submit keeps this same key after activation, so nothing you built breaks.

반환값: { app_key: string }Your APP_KEY — the shared secret used in every callback signature.

POST /api/games/draft-callback-testmethod
POST /api/games/draft-callback-test Authorization: Bearer <session-token> Content-Type: application/json

Runs the full integration self-test against your backend before a project exists: UTD sends genuinely signed payloads (with your draft app_key) to your three callbacks and validates every response against the exact contract above — envelope, errorCode 0, balance fields, forged-signature and foreign-token rejection. It then proves the balance actually MOVES: a small credit → replay of the same orderId (must be a no-op, no coin minting) → an equal debit that restores the balance — all net-zero on your test account. This catches a backend that returns 'success' with an unchanged balance (the #1 activation rejection). A passing test unlocks project submission for the exact (callback_base_url, test_uid, test_token) you tested; changing any of them invalidates the pass and you must re-test. Rate-limited to 6 calls per minute.

매개변수

  • callback_base_urlstring필수

    Your backend base URL hosting the three callbacks (https, publicly reachable).

  • test_uidstring필수

    A real test account id on YOUR platform.

  • test_tokenstring필수

    A valid session token for that test account (your backend must accept it in get-user-info).

반환값: { ok: boolean, checks: Array<{ name, url, ok, http_status, error, response }>, error_codes_contract: string }One check per callback with the exact failure reason when a response doesn't match the contract. ok: true = you can submit.

POST /api/games/projectsmethod
POST /api/games/projects Authorization: Bearer <session-token> Content-Type: multipart/form-data payload = JSON string of the fields below screenshots[] = 1–8 image files (jpg/png/webp, max 4MB each)

Submits a new games project for review from your dashboard session. Requires a verified phone on the account, and a passing draft callback test matching the submitted callback_base_url + test_uid + test_token exactly — otherwise 422 {"message":"callback_test_required"}. The request is multipart: all fields go JSON-encoded inside a single "payload" part, plus screenshots[] file parts. The project starts pending; the UTD team provisions it with the provider and approves it (status becomes active). Provider secrets are never exposed to your app.

매개변수

  • namestring필수

    Project display name (max 255).

  • callback_base_urlstring필수

    Your backend base URL that hosts the three game callbacks. Must match the URL that passed the draft callback test.

  • coin_priceinteger필수

    How many of your in-app coins equal 1 unit of currency (e.g. 13000 for 1 USD = 13,000 coins). coin values in the callbacks use this currency.

  • coin_currencystring

    Currency of coin_price (default USD, max 10 chars).

  • app_linksArray<{platform, url}>필수

    At least one live link to your app (store page or APK/site).

  • test_uid / test_tokenstring필수

    The test account that passed the draft callback test — the games team also uses it to verify your callbacks before activation.

  • languagesstring[]필수

    Game languages to enable (min 1): Arabic, English, Urdu, Indian, Turkish.

  • regionstring필수

    Where most of your users are, e.g. "Dubai" or "Doha" (max 120) — used to place game servers near them.

  • screen_sizesstring[]필수

    Game screen sizes to provision (min 1): full, HD, Half.

  • timezonestring필수

    Your app's timezone, e.g. "GMT+03" (max 32).

  • game_idsinteger[]필수

    Catalog ids of the games you want (1–8). Launches start with a maximum of 8 games — more are added later as your audience grows.

  • daily_active_usersinteger

    Approximate daily active users (optional).

  • requested_games_notestring

    Free-text list of games you want that are not in the catalog (max 2000 chars).

반환값: { project: { id, name, status, provider_app_id } }The created project in pending status, awaiting UTD review. 422 callback_test_required = run the draft callback test first with these exact values.

POST /api/games/projects/{id}/callback-testmethod
POST /api/games/projects/{id}/callback-test Authorization: Bearer <session-token>

Self-test for your three game callbacks (get-user-info / change-balance / make-up-orders). UTD sends genuinely signed payloads to your callback base URL using the project's test account. Beyond the contract checks (envelope, errorCode 0, forged-signature and foreign-token rejection), it proves the balance actually moves: a small credit → replay of the same orderId (must be a no-op — no coin minting) → an equal debit that restores the balance, all net-zero on your test account. This catches a backend that returns 'success' with an unchanged balance (the #1 activation rejection). Rate-limited to 6 calls per minute (429 → wait a minute). Requires callback_base_url, test_uid and test_token on the project.

반환값: { ok: boolean, checks: Array<{ name, url, ok, http_status, error, response }>, error_codes_contract: string }One entry per check: the three callbacks, the two negative checks (forged-signature-rejected, wrong-token-rejected), and the three balance-movement checks (balance-credit-applied, balance-idempotent, balance-debit-restored). error_codes_contract restates the rule: intentional blocking must return errorCode 30001; 4005 is reserved for real failures (provider monitoring alerts on it).

UTD와 함께 빌드할 준비가 되셨나요?

계정을 만들고, 마스터 지갑을 충전한 뒤, 필요한 서비스를 켜세요.