{
  "info": {
    "_postman_id": "5cdbfdb6-4449-4dc9-b7d3-8d0696027838",
    "name": "Rokt Session API — Generic S2S Template",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
    "description": "Generic **Rokt Session API (S2S)** template — the default/common attributes partners send, with the standard call flow. Fill in `rpub`, `rsec`, `rokt_account_id`, and `page_identifier`, then run **Get offers** → the event requests.\n\n**Auth (both calls use HTTP Basic):**\n- Offers and events both authenticate with `Basic base64(rpub:rsec)` (Postman encodes it from the Authorization tab).\n- Events additionally carry `single_session: true` + a per-event `session_id` (the top-level `session_id` from the offers response, auto-captured) — that's how the events attach to the offers session without a separate bearer token.\n\n**Headers:** `rokt-account-id` on every call; `rokt-platform-type` on the offers call (`Web` default — s2s defaults to Web; set `iOS`/`Android` for native apps, REQUIRED for native page detection — accepted values (case-insensitive): `iOS`, `Android`, `Web`, `WebDesktop`, `WebMobile`).\n\n**Default attribute set (what partners commonly send):**\n| Field | Where | Notes |\n|---|---|---|\n| `email` / `email_sha256` | customer / attributes | Partners send raw email (often web) OR a SHA-256 hash (often native). Template provides both from one generated identity. |\n| `first_name`, `last_name` | customer | Raw here; some native partners hash these (`firstnamesha256`). |\n| `country`, `language` | customer | |\n| `confirmation_ref`, `transaction_value`, `currency` | transaction | Order/confirmation reference + numeric value (commerce/confirmation pages). |\n| `user_agent`, `ip`, `language` | device | `user_agent` is a `user_agent` variable (default desktop Chrome) — change it to simulate iOS / Android / other clients. `ip` = the end-user's IP your server injects per request (`client_ip` var; defaults to a documentation IP). |\n| `device_language`, `locale` | attributes | |\n| `page_identifier` | page | Required — the page/view you're serving on. |\n| `package_name` | page | **Native only** (iOS/Android app bundle id). Required for native page detection; leave blank for web. |\n\nAdd any partner-specific signals under `attributes` (e.g. a merchant `source_domain`, an experiment `variant`, loyalty/travel fields).\n\n**Auto-generated per run** (you don't fill these): a fresh random identity (email + hash + name), `confirmation_ref`/`instance_id` GUIDs, the event `timestamp`, and the session/creative IDs captured from the offers response. A fresh identity each run avoids advertiser frequency-cap empties.\n\n**Testing (`rokt-test-session`):** send `rokt-test-session: true` on the offers/events calls to mark all resulting activity as reporting-only — it's tagged and filtered out of production metrics (it does NOT force an offer to serve), and there's no separate sandbox URL. The **Get offers — test session (validation)** request already has this header set, and each event request carries the same header disabled — enable it on the events while validating so the whole test flow stays out of production reporting. Disable/remove on both calls to go live.\n\nResponses: offers has no success/errors envelope (parse top-level); events return **202 Accepted** (may include non-fatal `warnings`). All values here are synthetic."
  },
  "variable": [
    {
      "key": "base_url",
      "value": "https://api.rokt.com",
      "type": "string"
    },
    {
      "key": "rokt_account_id",
      "value": "",
      "type": "string"
    },
    {
      "key": "rpub",
      "value": "",
      "type": "string"
    },
    {
      "key": "rsec",
      "value": "",
      "type": "string"
    },
    {
      "key": "rokt_platform_type",
      "value": "Web",
      "type": "string"
    },
    {
      "key": "page_identifier",
      "value": "",
      "type": "string"
    },
    {
      "key": "package_name",
      "value": "",
      "type": "string"
    },
    {
      "key": "user_agent",
      "value": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
      "type": "string"
    },
    {
      "key": "client_ip",
      "value": "198.51.100.10",
      "type": "string"
    },
    {
      "key": "session_token",
      "value": "",
      "type": "string"
    },
    {
      "key": "session_id",
      "value": "",
      "type": "string"
    },
    {
      "key": "page_instance_guid",
      "value": "",
      "type": "string"
    },
    {
      "key": "creative_instance_guid",
      "value": "",
      "type": "string"
    },
    {
      "key": "positive_response_instance_guid",
      "value": "",
      "type": "string"
    },
    {
      "key": "creative_event_token",
      "value": "",
      "type": "string"
    },
    {
      "key": "positive_event_token",
      "value": "",
      "type": "string"
    },
    {
      "key": "event_ts",
      "value": "",
      "type": "string"
    },
    {
      "key": "customer_email",
      "value": "",
      "type": "string"
    },
    {
      "key": "email_sha256",
      "value": "",
      "type": "string"
    }
  ],
  "item": [
    {
      "name": "Get offers",
      "event": [
        {
          "listen": "prerequest",
          "script": {
            "type": "text/javascript",
            "exec": [
              "// Fresh synthetic identity per run — a repeated identity hits advertiser frequency caps and returns no offers.",
              "// crypto-js is built into the Postman sandbox. Partners send EITHER raw email (typically web) OR a SHA-256",
              "// hash (typically native); this template provides both (customer.email + attributes.email_sha256) so it works either way.",
              "const CryptoJS = require('crypto-js');",
              "const email = pm.variables.replaceIn('{{$randomEmail}}').toLowerCase();",
              "pm.collectionVariables.set('customer_email', email);",
              "pm.collectionVariables.set('email_sha256', CryptoJS.SHA256(email).toString());"
            ]
          }
        },
        {
          "listen": "test",
          "script": {
            "type": "text/javascript",
            "exec": [
              "// Clear everything captured from a previous run FIRST — a no-fill must not",
              "// leave the event calls pointing at the previous session's elements.",
              "['session_token','session_id','page_instance_guid','creative_instance_guid',",
              " 'creative_event_token','positive_response_instance_guid','positive_event_token']",
              "  .forEach(function (k) { pm.collectionVariables.unset(k); });",
              "",
              "// 2xx offers response has NO envelope — parse fields off the top-level JSON and stash for the event calls.",
              "if (pm.response.code >= 200 && pm.response.code < 300) {",
              "  try {",
              "    var json = pm.response.json();",
              "    var t = json && json.session_token && json.session_token.token;",
              "    if (t) { pm.collectionVariables.set('session_token', t); }",
              "    if (json && json.session_id) { pm.collectionVariables.set('session_id', json.session_id); }",
              "    if (json && json.page_instance_guid) { pm.collectionVariables.set('page_instance_guid', json.page_instance_guid); }",
              "    try {",
              "      var s = json && json.plugins && json.plugins[0] && json.plugins[0].plugin && json.plugins[0].plugin.config",
              "              && json.plugins[0].plugin.config.slots && json.plugins[0].plugin.config.slots[0];",
              "      var cg = s && s.offer && s.offer.creative && s.offer.creative.instance_guid;",
              "      if (cg) { pm.collectionVariables.set('creative_instance_guid', cg); }",
              "      var ct = s && s.offer && s.offer.creative && s.offer.creative.token;",
              "      if (ct) { pm.collectionVariables.set('creative_event_token', ct); }",
              "      var pos = s && s.offer && s.offer.creative && s.offer.creative.response_options_map && s.offer.creative.response_options_map.positive;",
              "      if (pos && pos.instance_guid) { pm.collectionVariables.set('positive_response_instance_guid', pos.instance_guid); }",
              "      if (pos && pos.token) { pm.collectionVariables.set('positive_event_token', pos.token); }",
              "    } catch (e) { /* no-fill or different shape — leave those vars */ }",
              "  } catch (e) { /* body not JSON */ }",
              "}"
            ]
          }
        }
      ],
      "request": {
        "auth": {
          "type": "basic",
          "basic": [
            {
              "key": "username",
              "value": "{{rpub}}",
              "type": "string"
            },
            {
              "key": "password",
              "value": "{{rsec}}",
              "type": "string"
            }
          ]
        },
        "method": "POST",
        "header": [
          {
            "key": "Content-Type",
            "value": "application/json"
          },
          {
            "key": "rokt-account-id",
            "value": "{{rokt_account_id}}"
          },
          {
            "key": "rokt-platform-type",
            "value": "{{rokt_platform_type}}"
          }
        ],
        "body": {
          "mode": "raw",
          "raw": "{\n  \"channel\": {\n    \"type\": \"s2s\"\n  },\n  \"page\": {\n    \"page_identifier\": \"{{page_identifier}}\",\n    \"package_name\": \"{{package_name}}\"\n  },\n  \"customer\": {\n    \"email\": \"{{customer_email}}\",\n    \"first_name\": \"{{$randomFirstName}}\",\n    \"last_name\": \"{{$randomLastName}}\",\n    \"country\": \"US\",\n    \"language\": \"en\"\n  },\n  \"transaction\": {\n    \"confirmation_ref\": \"{{$guid}}\",\n    \"transaction_value\": 49.99,\n    \"currency\": \"USD\"\n  },\n  \"device\": {\n    \"user_agent\": \"{{user_agent}}\",\n    \"ip\": \"{{client_ip}}\",\n    \"language\": \"en\"\n  },\n  \"attributes\": {\n    \"email_sha256\": \"{{email_sha256}}\",\n    \"device_language\": \"en-US\",\n    \"locale\": \"en-US\"\n  }\n}",
          "options": {
            "raw": {
              "language": "json"
            }
          }
        },
        "url": {
          "raw": "{{base_url}}/v2/sessions/offers",
          "host": [
            "{{base_url}}"
          ],
          "path": [
            "v2",
            "sessions",
            "offers"
          ]
        },
        "description": "Fetch offers for a session. HTTP Basic (rpub/rsec); rokt-account-id + rokt-platform-type required. Sends the default typed shape (customer / transaction / device / page) + common attributes. Pre-request generates a fresh synthetic identity; the test script captures session_id / session_token / page_instance_guid / creative + positive-response GUIDs for the event calls."
      }
    },
    {
      "name": "Get offers — test session (validation)",
      "event": [
        {
          "listen": "prerequest",
          "script": {
            "type": "text/javascript",
            "exec": [
              "// Fresh synthetic identity per run — a repeated identity hits advertiser frequency caps and returns no offers.",
              "// crypto-js is built into the Postman sandbox. Partners send EITHER raw email (typically web) OR a SHA-256",
              "// hash (typically native); this template provides both (customer.email + attributes.email_sha256) so it works either way.",
              "const CryptoJS = require('crypto-js');",
              "const email = pm.variables.replaceIn('{{$randomEmail}}').toLowerCase();",
              "pm.collectionVariables.set('customer_email', email);",
              "pm.collectionVariables.set('email_sha256', CryptoJS.SHA256(email).toString());"
            ]
          }
        },
        {
          "listen": "test",
          "script": {
            "type": "text/javascript",
            "exec": [
              "// Clear everything captured from a previous run FIRST — a no-fill must not",
              "// leave the event calls pointing at the previous session's elements.",
              "['session_token','session_id','page_instance_guid','creative_instance_guid',",
              " 'creative_event_token','positive_response_instance_guid','positive_event_token']",
              "  .forEach(function (k) { pm.collectionVariables.unset(k); });",
              "",
              "// 2xx offers response has NO envelope — parse fields off the top-level JSON and stash for the event calls.",
              "if (pm.response.code >= 200 && pm.response.code < 300) {",
              "  try {",
              "    var json = pm.response.json();",
              "    var t = json && json.session_token && json.session_token.token;",
              "    if (t) { pm.collectionVariables.set('session_token', t); }",
              "    if (json && json.session_id) { pm.collectionVariables.set('session_id', json.session_id); }",
              "    if (json && json.page_instance_guid) { pm.collectionVariables.set('page_instance_guid', json.page_instance_guid); }",
              "    try {",
              "      var s = json && json.plugins && json.plugins[0] && json.plugins[0].plugin && json.plugins[0].plugin.config",
              "              && json.plugins[0].plugin.config.slots && json.plugins[0].plugin.config.slots[0];",
              "      var cg = s && s.offer && s.offer.creative && s.offer.creative.instance_guid;",
              "      if (cg) { pm.collectionVariables.set('creative_instance_guid', cg); }",
              "      var ct = s && s.offer && s.offer.creative && s.offer.creative.token;",
              "      if (ct) { pm.collectionVariables.set('creative_event_token', ct); }",
              "      var pos = s && s.offer && s.offer.creative && s.offer.creative.response_options_map && s.offer.creative.response_options_map.positive;",
              "      if (pos && pos.instance_guid) { pm.collectionVariables.set('positive_response_instance_guid', pos.instance_guid); }",
              "      if (pos && pos.token) { pm.collectionVariables.set('positive_event_token', pos.token); }",
              "    } catch (e) { /* no-fill or different shape — leave those vars */ }",
              "  } catch (e) { /* body not JSON */ }",
              "}"
            ]
          }
        }
      ],
      "request": {
        "auth": {
          "type": "basic",
          "basic": [
            {
              "key": "username",
              "value": "{{rpub}}",
              "type": "string"
            },
            {
              "key": "password",
              "value": "{{rsec}}",
              "type": "string"
            }
          ]
        },
        "method": "POST",
        "header": [
          {
            "key": "Content-Type",
            "value": "application/json"
          },
          {
            "key": "rokt-account-id",
            "value": "{{rokt_account_id}}"
          },
          {
            "key": "rokt-platform-type",
            "value": "{{rokt_platform_type}}"
          },
          {
            "key": "rokt-test-session",
            "value": "true"
          }
        ],
        "body": {
          "mode": "raw",
          "raw": "{\n  \"channel\": {\n    \"type\": \"s2s\"\n  },\n  \"page\": {\n    \"page_identifier\": \"{{page_identifier}}\",\n    \"package_name\": \"{{package_name}}\"\n  },\n  \"customer\": {\n    \"email\": \"{{customer_email}}\",\n    \"first_name\": \"{{$randomFirstName}}\",\n    \"last_name\": \"{{$randomLastName}}\",\n    \"country\": \"US\",\n    \"language\": \"en\"\n  },\n  \"transaction\": {\n    \"confirmation_ref\": \"{{$guid}}\",\n    \"transaction_value\": 49.99,\n    \"currency\": \"USD\"\n  },\n  \"device\": {\n    \"user_agent\": \"{{user_agent}}\",\n    \"ip\": \"{{client_ip}}\",\n    \"language\": \"en\"\n  },\n  \"attributes\": {\n    \"email_sha256\": \"{{email_sha256}}\",\n    \"device_language\": \"en-US\",\n    \"locale\": \"en-US\"\n  }\n}",
          "options": {
            "raw": {
              "language": "json"
            }
          }
        },
        "url": {
          "raw": "{{base_url}}/v2/sessions/offers",
          "host": [
            "{{base_url}}"
          ],
          "path": [
            "v2",
            "sessions",
            "offers"
          ]
        },
        "description": "Same as Get offers but adds `rokt-test-session: true` so all resulting activity is reporting-only. Use to validate request shape / auth / wiring."
      }
    },
    {
      "name": "Report event — impression",
      "event": [
        {
          "listen": "prerequest",
          "script": {
            "type": "text/javascript",
            "exec": [
              "// fresh millisecond timestamp per send — avoids the >24h drift warning",
              "pm.collectionVariables.set('event_ts', Date.now());"
            ]
          }
        }
      ],
      "request": {
        "auth": {
          "type": "basic",
          "basic": [
            {
              "key": "username",
              "value": "{{rpub}}",
              "type": "string"
            },
            {
              "key": "password",
              "value": "{{rsec}}",
              "type": "string"
            }
          ]
        },
        "method": "POST",
        "header": [
          {
            "key": "Content-Type",
            "value": "application/json"
          },
          {
            "key": "rokt-account-id",
            "value": "{{rokt_account_id}}"
          },
          {
            "key": "rokt-test-session",
            "value": "true",
            "disabled": true,
            "description": "Enable during validation runs (paired with 'Get offers — test session') so the events are tagged reporting-only and stay out of production metrics. Keep disabled for live traffic."
          }
        ],
        "body": {
          "mode": "raw",
          "raw": "{\n  \"channel\": {\n    \"type\": \"s2s\"\n  },\n  \"single_session\": true,\n  \"events\": [\n    {\n      \"event_type\": \"impression\",\n      \"instance_id\": \"{{$guid}}\",\n      \"session_id\": \"{{session_id}}\",\n      \"timestamp\": {{event_ts}},\n      \"data\": {\n        \"parent_id\": \"{{creative_instance_guid}}\",\n        \"page_instance_guid\": \"{{page_instance_guid}}\",\n        \"token\": \"{{creative_event_token}}\"\n      }\n    }\n  ]\n}",
          "options": {
            "raw": {
              "language": "json"
            }
          }
        },
        "url": {
          "raw": "{{base_url}}/v2/sessions/events",
          "host": [
            "{{base_url}}"
          ],
          "path": [
            "v2",
            "sessions",
            "events"
          ]
        },
        "description": "Impression on the served creative. HTTP Basic + single_session + per-event session_id. parent_id = the creative's instance_guid (echo whatever the offers response returned, verbatim — it may be prefixed, e.g. `ad:<uuid>`). Fresh {{$guid}} instance_id + current timestamp per send. Returns 202."
      }
    },
    {
      "name": "Report event — viewed",
      "event": [
        {
          "listen": "prerequest",
          "script": {
            "type": "text/javascript",
            "exec": [
              "// fresh millisecond timestamp per send — avoids the >24h drift warning",
              "pm.collectionVariables.set('event_ts', Date.now());"
            ]
          }
        }
      ],
      "request": {
        "auth": {
          "type": "basic",
          "basic": [
            {
              "key": "username",
              "value": "{{rpub}}",
              "type": "string"
            },
            {
              "key": "password",
              "value": "{{rsec}}",
              "type": "string"
            }
          ]
        },
        "method": "POST",
        "header": [
          {
            "key": "Content-Type",
            "value": "application/json"
          },
          {
            "key": "rokt-account-id",
            "value": "{{rokt_account_id}}"
          },
          {
            "key": "rokt-test-session",
            "value": "true",
            "disabled": true,
            "description": "Enable during validation runs (paired with 'Get offers — test session') so the events are tagged reporting-only and stay out of production metrics. Keep disabled for live traffic."
          }
        ],
        "body": {
          "mode": "raw",
          "raw": "{\n  \"channel\": {\n    \"type\": \"s2s\"\n  },\n  \"single_session\": true,\n  \"events\": [\n    {\n      \"event_type\": \"viewed\",\n      \"instance_id\": \"{{$guid}}\",\n      \"session_id\": \"{{session_id}}\",\n      \"timestamp\": {{event_ts}},\n      \"data\": {\n        \"parent_id\": \"{{creative_instance_guid}}\",\n        \"page_instance_guid\": \"{{page_instance_guid}}\",\n        \"token\": \"{{creative_event_token}}\"\n      }\n    }\n  ]\n}",
          "options": {
            "raw": {
              "language": "json"
            }
          }
        },
        "url": {
          "raw": "{{base_url}}/v2/sessions/events",
          "host": [
            "{{base_url}}"
          ],
          "path": [
            "v2",
            "sessions",
            "events"
          ]
        },
        "description": "Viewability event for the creative. Same auth/shape as impression."
      }
    },
    {
      "name": "Report event — signal_response (positive)",
      "event": [
        {
          "listen": "prerequest",
          "script": {
            "type": "text/javascript",
            "exec": [
              "// fresh millisecond timestamp per send — avoids the >24h drift warning",
              "pm.collectionVariables.set('event_ts', Date.now());"
            ]
          }
        }
      ],
      "request": {
        "auth": {
          "type": "basic",
          "basic": [
            {
              "key": "username",
              "value": "{{rpub}}",
              "type": "string"
            },
            {
              "key": "password",
              "value": "{{rsec}}",
              "type": "string"
            }
          ]
        },
        "method": "POST",
        "header": [
          {
            "key": "Content-Type",
            "value": "application/json"
          },
          {
            "key": "rokt-account-id",
            "value": "{{rokt_account_id}}"
          },
          {
            "key": "rokt-test-session",
            "value": "true",
            "disabled": true,
            "description": "Enable during validation runs (paired with 'Get offers — test session') so the events are tagged reporting-only and stay out of production metrics. Keep disabled for live traffic."
          }
        ],
        "body": {
          "mode": "raw",
          "raw": "{\n  \"channel\": {\n    \"type\": \"s2s\"\n  },\n  \"single_session\": true,\n  \"events\": [\n    {\n      \"event_type\": \"signal_response\",\n      \"instance_id\": \"{{$guid}}\",\n      \"session_id\": \"{{session_id}}\",\n      \"timestamp\": {{event_ts}},\n      \"data\": {\n        \"parent_id\": \"{{positive_response_instance_guid}}\",\n        \"page_instance_guid\": \"{{page_instance_guid}}\",\n        \"token\": \"{{positive_event_token}}\"\n      }\n    }\n  ]\n}",
          "options": {
            "raw": {
              "language": "json"
            }
          }
        },
        "url": {
          "raw": "{{base_url}}/v2/sessions/events",
          "host": [
            "{{base_url}}"
          ],
          "path": [
            "v2",
            "sessions",
            "events"
          ]
        },
        "description": "Positive response (customer engaged). parent_id = the positive response option's instance_guid; data.token = its event token (both auto-captured). Returns 202."
      }
    }
  ]
}