{
  "info": {
    "name": "Cloud1x2 CGSP v1.1",
    "description": "The Cloud1x2 Game Service Protocol (CGSP) v1.1 — every internal endpoint, grouped by surface.\n\nTwo base URLs are used because CGSP endpoints flow in two directions:\n\n- **Control Plane** requests target `{{serviceBaseUrl}}` — the game service hosts these; sending them simulates the platform driving the service.\n- **Config + Ledger**, **Launch Verification**, and **Wallet** requests target `{{platformBaseUrl}}` — the platform hosts these; sending them simulates the service calling the platform.\n\nImport the companion environment (cloud1x2-cgsp.postman_environment.json) or edit the collection variables below. Secrets default to `dev` for local use.\n\nDocs: https://github.com/muratbolten/cloud1x2",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
  },
  "variable": [
    { "key": "serviceBaseUrl", "value": "http://localhost:3030", "type": "string" },
    { "key": "platformBaseUrl", "value": "http://localhost:7000", "type": "string" },
    { "key": "controlSecret", "value": "dev", "type": "string" },
    { "key": "configSecret", "value": "dev", "type": "string" },
    { "key": "walletSecret", "value": "dev", "type": "string" },
    { "key": "verifyLaunchSecret", "value": "dev", "type": "string" },
    { "key": "serviceUid", "value": "wp9dvdgp4wfqmxq4", "type": "string" },
    { "key": "engineId", "value": "wp9dvdgp4wfqmxq4:p:42", "type": "string" },
    { "key": "runtimeProfileId", "value": "42", "type": "string" },
    { "key": "gameExternalId", "value": "9000", "type": "string" },
    { "key": "betUid", "value": "wp9dvdgp4wfqmxq4-01krrh7xn3qq5e69gn58wkjt38", "type": "string" },
    { "key": "partnerId", "value": "1", "type": "string" },
    { "key": "operatorId", "value": "cx-12", "type": "string" },
    { "key": "currency", "value": "EUR", "type": "string" },
    { "key": "customer", "value": "3773", "type": "string" },
    { "key": "playerToken", "value": "d315fa12770e65eb4af38c589aed0d", "type": "string" }
  ],
  "item": [
    {
      "name": "1. Control Plane (Platform → Service)",
      "description": "Endpoints the game service hosts. These requests target {{serviceBaseUrl}} and simulate the platform driving the service. Auth: x-internal-control-secret (except /health).",
      "item": [
        {
          "name": "PUT desired-state",
          "request": {
            "method": "PUT",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "x-internal-control-secret", "value": "{{controlSecret}}" }
            ],
            "url": {
              "raw": "{{serviceBaseUrl}}/internal/engines/desired-state",
              "host": ["{{serviceBaseUrl}}"],
              "path": ["internal", "engines", "desired-state"]
            },
            "body": {
              "mode": "raw",
              "options": { "raw": { "language": "json" } },
              "raw": "{\n  \"protocolVersion\": \"1.1\",\n  \"serviceUid\": \"{{serviceUid}}\",\n  \"engines\": [\n    {\n      \"engineId\": \"{{engineId}}\",\n      \"engineType\": \"{{serviceUid}}\",\n      \"runtimeProfileId\": {{runtimeProfileId}},\n      \"canonicalGameExternalId\": {{gameExternalId}},\n      \"serviceUid\": \"{{serviceUid}}\",\n      \"memberGames\": [\n        { \"gameExternalId\": {{gameExternalId}}, \"name\": \"Hi-Lo Classic\", \"skinKey\": \"default\", \"suspended\": false },\n        { \"gameExternalId\": 9001, \"name\": \"Hi-Lo Express\", \"skinKey\": \"neon\", \"suspended\": true }\n      ]\n    }\n  ]\n}"
            },
            "description": "The platform's full desired engine set. Idempotent: the service reconciles toward it. Pushed every 60s as a heartbeat."
          }
        },
        {
          "name": "GET engines",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{serviceBaseUrl}}/internal/engines",
              "host": ["{{serviceBaseUrl}}"],
              "path": ["internal", "engines"]
            },
            "description": "Diagnostic list of in-memory engines. No auth (assumed internal network)."
          }
        },
        {
          "name": "GET engine status",
          "request": {
            "method": "GET",
            "header": [
              { "key": "x-internal-control-secret", "value": "{{controlSecret}}" }
            ],
            "url": {
              "raw": "{{serviceBaseUrl}}/internal/engines/:engineId/status",
              "host": ["{{serviceBaseUrl}}"],
              "path": ["internal", "engines", ":engineId", "status"],
              "variable": [
                { "key": "engineId", "value": "{{engineId}}", "description": "URL-encoded engineId, e.g. wp9dvdgp4wfqmxq4%3Ap%3A42" }
              ]
            },
            "description": "Detail for one engine. Only engineId + status are required in the response."
          }
        },
        {
          "name": "GET health",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{serviceBaseUrl}}/health",
              "host": ["{{serviceBaseUrl}}"],
              "path": ["health"]
            },
            "description": "Liveness check. No auth. Must return the engines map (empty object if none)."
          }
        },
        {
          "name": "POST engine pause",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "x-internal-control-secret", "value": "{{controlSecret}}" }
            ],
            "url": {
              "raw": "{{serviceBaseUrl}}/internal/engines/:engineId/pause",
              "host": ["{{serviceBaseUrl}}"],
              "path": ["internal", "engines", ":engineId", "pause"],
              "variable": [
                { "key": "engineId", "value": "{{engineId}}", "description": "URL-encoded engineId" }
              ]
            },
            "body": {
              "mode": "raw",
              "options": { "raw": { "language": "json" } },
              "raw": "{\n  \"reason\": \"Runtime profile refresh\",\n  \"adminId\": 17\n}"
            },
            "description": "v1.1. Admin-triggered pause. The current round finishes, then the engine moves to paused. Pause state is service-owned."
          }
        },
        {
          "name": "POST engine resume",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "x-internal-control-secret", "value": "{{controlSecret}}" }
            ],
            "url": {
              "raw": "{{serviceBaseUrl}}/internal/engines/:engineId/resume",
              "host": ["{{serviceBaseUrl}}"],
              "path": ["internal", "engines", ":engineId", "resume"],
              "variable": [
                { "key": "engineId", "value": "{{engineId}}", "description": "URL-encoded engineId" }
              ]
            },
            "body": {
              "mode": "raw",
              "options": { "raw": { "language": "json" } },
              "raw": "{\n  \"adminId\": 17\n}"
            },
            "description": "v1.1. Admin-triggered resume. The engine moves paused → running."
          }
        }
      ]
    },
    {
      "name": "2. Config + Ledger (Service → Platform)",
      "description": "Platform endpoints the service calls. These requests target {{platformBaseUrl}}.",
      "item": [
        {
          "name": "GET runtime-profile",
          "request": {
            "method": "GET",
            "header": [
              { "key": "x-internal-config-secret", "value": "{{configSecret}}" }
            ],
            "url": {
              "raw": "{{platformBaseUrl}}/internal/runtime-profiles/:id",
              "host": ["{{platformBaseUrl}}"],
              "path": ["internal", "runtime-profiles", ":id"],
              "variable": [
                { "key": "id", "value": "{{runtimeProfileId}}" }
              ]
            },
            "description": "Full runtime profile. Returns { ok, protocolVersion, profile (snake_case + config_json), botCurrencies }. Cache the result."
          }
        },
        {
          "name": "GET bet-templates",
          "request": {
            "method": "GET",
            "header": [
              { "key": "x-internal-config-secret", "value": "{{configSecret}}" }
            ],
            "url": {
              "raw": "{{platformBaseUrl}}/internal/bet-templates?serviceUid={{serviceUid}}&partnerId={{partnerId}}&operatorId={{operatorId}}&gameExternalId={{gameExternalId}}&currency={{currency}}",
              "host": ["{{platformBaseUrl}}"],
              "path": ["internal", "bet-templates"],
              "query": [
                { "key": "serviceUid", "value": "{{serviceUid}}" },
                { "key": "partnerId", "value": "{{partnerId}}" },
                { "key": "operatorId", "value": "{{operatorId}}" },
                { "key": "gameExternalId", "value": "{{gameExternalId}}" },
                { "key": "currency", "value": "{{currency}}" }
              ]
            },
            "description": "v1.1. Side-effect-free per-round bet-limit fetch. Returns { ok, betMin, betMax, maxWin, quickBets }."
          }
        },
        {
          "name": "POST consolidated/tx (open ledger)",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "x-internal-wallet-secret", "value": "{{walletSecret}}" }
            ],
            "url": {
              "raw": "{{platformBaseUrl}}/internal/consolidated/tx",
              "host": ["{{platformBaseUrl}}"],
              "path": ["internal", "consolidated", "tx"]
            },
            "body": {
              "mode": "raw",
              "options": { "raw": { "language": "json" } },
              "raw": "{\n  \"engineType\": \"{{serviceUid}}\",\n  \"serviceBetUid\": \"{{betUid}}\",\n  \"gameExternalId\": {{gameExternalId}},\n  \"partnerId\": {{partnerId}},\n  \"operatorId\": \"{{operatorId}}\",\n  \"playerToken\": \"{{playerToken}}\",\n  \"customer\": \"{{customer}}\",\n  \"currency\": \"{{currency}}\",\n  \"betAmount\": 10.00,\n  \"winAmount\": 0,\n  \"reportingCurrency\": \"{{currency}}\",\n  \"rateToReporting\": 1.0,\n  \"debitTrxId\": \"debit-{{betUid}}\",\n  \"creditTrxId\": null,\n  \"rollbackTrxId\": null,\n  \"status\": \"open\",\n  \"isDemo\": false,\n  \"walletB2bLog\": {\n    \"kind\": \"debit\",\n    \"entry\": { \"request\": {}, \"response\": {}, \"requestTimestamp\": \"2026-05-19T10:00:00.123Z\", \"responseTimestamp\": \"2026-05-19T10:00:00.245Z\" }\n  }\n}"
            },
            "description": "Open a ledger record when a bet is placed. Idempotent on (engineType, serviceBetUid). status enum: open | won | lost | cancelled | rolled_back."
          }
        },
        {
          "name": "POST consolidated/tx/settle",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "x-internal-wallet-secret", "value": "{{walletSecret}}" }
            ],
            "url": {
              "raw": "{{platformBaseUrl}}/internal/consolidated/tx/settle",
              "host": ["{{platformBaseUrl}}"],
              "path": ["internal", "consolidated", "tx", "settle"]
            },
            "body": {
              "mode": "raw",
              "options": { "raw": { "language": "json" } },
              "raw": "{\n  \"engineType\": \"{{serviceUid}}\",\n  \"serviceBetUid\": \"{{betUid}}\",\n  \"status\": \"won\",\n  \"winAmount\": 19.50,\n  \"creditTrxId\": \"credit-{{betUid}}\",\n  \"rollbackTrxId\": null,\n  \"walletB2bLog\": {\n    \"kind\": \"credit\",\n    \"entry\": { \"request\": {}, \"response\": {}, \"requestTimestamp\": \"2026-05-19T10:00:05.000Z\", \"responseTimestamp\": \"2026-05-19T10:00:05.120Z\" }\n  }\n}"
            },
            "description": "Settle a bet. status: won | lost | cancelled | rolled_back (note the underscore). A loss also sets creditTrxId with winAmount 0."
          }
        },
        {
          "name": "POST round-start-check",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "x-internal-control-secret", "value": "{{controlSecret}}" }
            ],
            "url": {
              "raw": "{{platformBaseUrl}}/internal/engines/round-start-check",
              "host": ["{{platformBaseUrl}}"],
              "path": ["internal", "engines", "round-start-check"]
            },
            "body": {
              "mode": "raw",
              "options": { "raw": { "language": "json" } },
              "raw": "{\n  \"serviceUid\": \"{{serviceUid}}\",\n  \"engineId\": \"{{engineId}}\"\n}"
            },
            "description": "v1.1. Per-round pull of the platform's suspend lists. Returns { suspendedPartners, suspendedOperators }. Called by the service at round start."
          }
        }
      ]
    },
    {
      "name": "3. Launch Verification (Service → Platform)",
      "description": "The gateway to all gameplay. These requests target {{platformBaseUrl}}.",
      "item": [
        {
          "name": "POST verify-launch (real)",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "x-internal-verify-launch-secret", "value": "{{verifyLaunchSecret}}" }
            ],
            "url": {
              "raw": "{{platformBaseUrl}}/internal/verify-launch?for={{serviceUid}}",
              "host": ["{{platformBaseUrl}}"],
              "path": ["internal", "verify-launch"],
              "query": [
                { "key": "for", "value": "{{serviceUid}}", "description": "Preferred: the service_uid. Backward compat: the service slug." }
              ]
            },
            "body": {
              "mode": "raw",
              "options": { "raw": { "language": "json" } },
              "raw": "{\n  \"gameId\": {{gameExternalId}},\n  \"demo\": false,\n  \"currency\": \"{{currency}}\",\n  \"token\": \"{{playerToken}}\",\n  \"customer\": \"{{customer}}\",\n  \"organizationId\": 1903,\n  \"trader\": \"{{operatorId}}\",\n  \"locale\": \"en\"\n}"
            },
            "description": "Real mode: gameId, currency, token, customer, organizationId, trader are all required. Returns a flat session object."
          }
        },
        {
          "name": "POST verify-launch (demo)",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "x-internal-verify-launch-secret", "value": "{{verifyLaunchSecret}}" }
            ],
            "url": {
              "raw": "{{platformBaseUrl}}/internal/verify-launch?for={{serviceUid}}",
              "host": ["{{platformBaseUrl}}"],
              "path": ["internal", "verify-launch"],
              "query": [
                { "key": "for", "value": "{{serviceUid}}" }
              ]
            },
            "body": {
              "mode": "raw",
              "options": { "raw": { "language": "json" } },
              "raw": "{\n  \"gameId\": {{gameExternalId}},\n  \"demo\": true\n}"
            },
            "description": "Demo mode: only gameId is required. Currency, balance, and limits come from the runtime profile."
          }
        }
      ]
    },
    {
      "name": "4. Wallet (Service → Platform → Operator)",
      "description": "Real-money movements. These requests target {{platformBaseUrl}}. Responses are two-layer: ok (platform) + httpOk (operator). Not called in demo mode.",
      "item": [
        {
          "name": "POST wallet/debit",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "x-internal-wallet-secret", "value": "{{walletSecret}}" }
            ],
            "url": {
              "raw": "{{platformBaseUrl}}/internal/wallet/debit",
              "host": ["{{platformBaseUrl}}"],
              "path": ["internal", "wallet", "debit"]
            },
            "body": {
              "mode": "raw",
              "options": { "raw": { "language": "json" } },
              "raw": "{\n  \"partnerId\": {{partnerId}},\n  \"betId\": \"{{betUid}}\",\n  \"gameId\": {{gameExternalId}},\n  \"amount\": 10.00,\n  \"token\": \"{{playerToken}}\",\n  \"trxId\": \"debit-{{betUid}}\",\n  \"currency\": \"{{currency}}\",\n  \"customer\": \"{{customer}}\"\n}"
            },
            "description": "Take money on bet placement. On httpOk:false the operator refused — reject the bet, do not open the ledger."
          }
        },
        {
          "name": "POST wallet/credit",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "x-internal-wallet-secret", "value": "{{walletSecret}}" }
            ],
            "url": {
              "raw": "{{platformBaseUrl}}/internal/wallet/credit",
              "host": ["{{platformBaseUrl}}"],
              "path": ["internal", "wallet", "credit"]
            },
            "body": {
              "mode": "raw",
              "options": { "raw": { "language": "json" } },
              "raw": "{\n  \"partnerId\": {{partnerId}},\n  \"betId\": \"{{betUid}}\",\n  \"gameId\": {{gameExternalId}},\n  \"amount\": 19.50,\n  \"token\": \"{{playerToken}}\",\n  \"trxId\": \"credit-{{betUid}}\",\n  \"currency\": \"{{currency}}\",\n  \"customer\": \"{{customer}}\"\n}"
            },
            "description": "Settle a bet. amount = win on a win; amount = 0 signals a loss (bet closed, no payout)."
          }
        },
        {
          "name": "POST wallet/rollback",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "x-internal-wallet-secret", "value": "{{walletSecret}}" }
            ],
            "url": {
              "raw": "{{platformBaseUrl}}/internal/wallet/rollback",
              "host": ["{{platformBaseUrl}}"],
              "path": ["internal", "wallet", "rollback"]
            },
            "body": {
              "mode": "raw",
              "options": { "raw": { "language": "json" } },
              "raw": "{\n  \"partnerId\": {{partnerId}},\n  \"betId\": \"{{betUid}}\",\n  \"gameId\": {{gameExternalId}},\n  \"amount\": 10.00,\n  \"token\": \"{{playerToken}}\",\n  \"trxId\": \"rollback-{{betUid}}\",\n  \"customer\": \"{{customer}}\"\n}"
            },
            "description": "Reverse a debit on error. No currency field (the operator knows it from the original trxId)."
          }
        }
      ]
    }
  ]
}
