{
  "info": {
    "name": "Twin Bridge API",
    "description": "B2B bridge between crypto providers and licensed VASPs.\n\n## Before you start\n\n1. Set collection variables: `baseUrl`, `apiKey`, `apiSecret`.\n2. Requests under **Authenticated** use a pre-request script that signs each call with HMAC-SHA256 — nothing to configure per-request.\n3. `Idempotency-Key` is auto-generated as a UUID v4 on every send. Override manually on the request if you want to test replay.\n\n## Signature format (outbound)\n\n```\nbody_hash  = sha256_hex(raw_body)\nmessage    = timestamp + METHOD + PATH + body_hash\nsignature  = hmac_sha256(apiSecret, message)\n```\n\nSee `samples/hmac-sign.{go,js,php}` for reference implementations.\n",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
    "_postman_id": "c5e9a7d2-twin-bridge-b2b"
  },
  "variable": [
    { "key": "baseUrl",   "value": "http://localhost:3000" },
    { "key": "apiKey",    "value": "tb_demo_key_replace_me" },
    { "key": "apiSecret", "value": "tb_demo_secret_replace_me" }
  ],
  "auth": { "type": "noauth" },
  "event": [
    {
      "listen": "prerequest",
      "script": {
        "type": "text/javascript",
        "exec": [
          "// Shared pre-request: signs the request with HMAC-SHA256.",
          "// Only runs for requests tagged with `tb-auth` via collection variable `tb-auth=true`.",
          "const needsAuth = pm.request.headers.has('X-Tb-Auth');",
          "if (!needsAuth) return;",
          "pm.request.headers.remove('X-Tb-Auth');",
          "",
          "const CryptoJS = require('crypto-js');",
          "const apiKey    = pm.variables.get('apiKey');",
          "const apiSecret = pm.variables.get('apiSecret');",
          "",
          "const method    = pm.request.method;",
          "const urlObj    = pm.request.url;",
          "const path      = '/' + urlObj.path.join('/');",
          "const bodyRaw   = pm.request.body && pm.request.body.raw ? pm.request.body.raw : '';",
          "const timestamp = Math.floor(Date.now() / 1000).toString();",
          "",
          "const bodyHash  = CryptoJS.SHA256(bodyRaw).toString(CryptoJS.enc.Hex);",
          "const message   = timestamp + method + path + bodyHash;",
          "const signature = CryptoJS.HmacSHA256(message, apiSecret).toString(CryptoJS.enc.Hex);",
          "",
          "// UUID v4 for Idempotency-Key (POST only — harmless on GET)",
          "const uuid = () => {",
          "  return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {",
          "    const r = Math.random() * 16 | 0;",
          "    const v = c === 'x' ? r : (r & 0x3 | 0x8);",
          "    return v.toString(16);",
          "  });",
          "};",
          "",
          "pm.request.headers.upsert({ key: 'X-API-Key',       value: apiKey });",
          "pm.request.headers.upsert({ key: 'X-Signature',     value: signature });",
          "pm.request.headers.upsert({ key: 'X-Timestamp',     value: timestamp });",
          "pm.request.headers.upsert({ key: 'X-Sandbox',       value: 'true' });",
          "if (method === 'POST' && !pm.request.headers.has('Idempotency-Key')) {",
          "  pm.request.headers.upsert({ key: 'Idempotency-Key', value: uuid() });",
          "}"
        ]
      }
    }
  ],
  "item": [
    {
      "name": "Health (no auth)",
      "request": {
        "method": "GET",
        "header": [],
        "url": { "raw": "{{baseUrl}}/v1/health", "host": ["{{baseUrl}}"], "path": ["v1", "health"] }
      }
    },
    {
      "name": "Authenticated",
      "description": "All requests here auto-sign via the collection-level pre-request script. Marker header `X-Tb-Auth: 1` triggers signing and is removed before send.",
      "item": [
        {
          "name": "Create transaction — ON_RAMP (KGS → USDT)",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "X-Tb-Auth",    "value": "1" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"direction\":       \"ON_RAMP\",\n  \"fiat_amount\":     \"5000.00\",\n  \"fiat_currency\":   \"KGS\",\n  \"crypto_currency\": \"USDT\",\n  \"network\":         \"TRC-20\",\n  \"flow\":            \"KGS_FLOW\",\n  \"wallet_address\":  \"TRx7KzjN4GqLhU3k9pF2mW5vB8dY6cE1n\",\n  \"kyc_share_token\": \"sumsub_demo_token\"\n}"
            },
            "url": { "raw": "{{baseUrl}}/v1/transactions", "host": ["{{baseUrl}}"], "path": ["v1", "transactions"] }
          }
        },
        {
          "name": "Create transaction — OFF_RAMP (USDT → KGS)",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "X-Tb-Auth",    "value": "1" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"direction\":             \"OFF_RAMP\",\n  \"crypto_amount\":         \"50.00\",\n  \"fiat_currency\":         \"KGS\",\n  \"crypto_currency\":       \"USDT\",\n  \"network\":               \"TRC-20\",\n  \"flow\":                  \"KGS_FLOW\",\n  \"recipient_kgs_wallet\":  \"+996700123456\",\n  \"kyc_share_token\":       \"sumsub_demo_token\"\n}"
            },
            "url": { "raw": "{{baseUrl}}/v1/transactions", "host": ["{{baseUrl}}"], "path": ["v1", "transactions"] }
          }
        },
        {
          "name": "Get transaction by ID",
          "request": {
            "method": "GET",
            "header": [ { "key": "X-Tb-Auth", "value": "1" } ],
            "url": {
              "raw": "{{baseUrl}}/v1/transactions/:id",
              "host": ["{{baseUrl}}"],
              "path": ["v1", "transactions", ":id"],
              "variable": [ { "key": "id", "value": "550e8400-e29b-41d4-a716-446655440000" } ]
            }
          }
        },
        {
          "name": "List transactions",
          "request": {
            "method": "GET",
            "header": [ { "key": "X-Tb-Auth", "value": "1" } ],
            "url": {
              "raw": "{{baseUrl}}/v1/transactions?limit=20&page=1",
              "host": ["{{baseUrl}}"],
              "path": ["v1", "transactions"],
              "query": [
                { "key": "limit", "value": "20" },
                { "key": "page",  "value": "1" }
              ]
            }
          }
        },
        {
          "name": "Cancel transaction",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "X-Tb-Auth",    "value": "1" }
            ],
            "body": { "mode": "raw", "raw": "{}" },
            "url": {
              "raw": "{{baseUrl}}/v1/transactions/:id/cancel",
              "host": ["{{baseUrl}}"],
              "path": ["v1", "transactions", ":id", "cancel"],
              "variable": [ { "key": "id", "value": "550e8400-e29b-41d4-a716-446655440000" } ]
            }
          }
        },
        {
          "name": "Retry transaction",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "X-Tb-Auth",    "value": "1" }
            ],
            "body": { "mode": "raw", "raw": "{}" },
            "url": {
              "raw": "{{baseUrl}}/v1/transactions/:id/retry",
              "host": ["{{baseUrl}}"],
              "path": ["v1", "transactions", ":id", "retry"],
              "variable": [ { "key": "id", "value": "550e8400-e29b-41d4-a716-446655440000" } ]
            }
          }
        }
      ]
    }
  ]
}
