{
    "info": {
        "name": "Launch Pad - Partner Mode",
        "description": "Runnable examples for the Launch Pad **partner integration** (provisioning + SSO launch).\n\nThe flow is a textbook **OAuth 2.0 pre-authorized code** handshake: your backend authenticates server-to-server with an API key, provisions the customer, then mints a short-lived single-use code that the browser exchanges for a session.\n\n## Setup\n1. Set the `baseUrl` variable (default `http://localhost:5555`; use `https://app.aws-dev.vergetech.dev` for the hosted dev environment).\n2. Set `apiKey` to an `X-API-KEY` belonging to a **Partner Admin** user (an Admin with the `partnerAdmin` setting).\n3. Optionally change `partnerUserId` / `partnerCompanyId` - these are YOUR own ids for the customer; the API is idempotent on them.\n\n## Order\nRun the requests top to bottom. **Create launch** saves `launchCode` and `launchPadUserId` automatically and logs the browser launch URL to the Postman console; **Exchange launch code** consumes them.\n\nAll ids you send (`partnerUserId`, `partnerCompanyId`) are your own external identifiers - Launch Pad resolves them to its records and never asks you to store Launch Pad's ids.",
        "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
    },
    "variable": [
        { "key": "baseUrl", "value": "http://localhost:5555", "type": "string" },
        { "key": "apiKey", "value": "", "type": "string" },
        { "key": "partnerUserId", "value": "demo-user-001", "type": "string" },
        { "key": "partnerCompanyId", "value": "demo-co-001", "type": "string" },
        { "key": "launchCode", "value": "", "type": "string" },
        { "key": "launchPadUserId", "value": "", "type": "string" },
        { "key": "launchUrl", "value": "", "type": "string" }
    ],
    "item": [
        {
            "name": "1. Provision user",
            "event": [
                {
                    "listen": "test",
                    "script": {
                        "type": "text/javascript",
                        "exec": [
                            "pm.test('200 OK', () => pm.response.to.have.status(200));",
                            "pm.test('returns a Launch Pad userId', () => pm.expect(pm.response.json().userId).to.be.a('string'));"
                        ]
                    }
                }
            ],
            "request": {
                "method": "POST",
                "header": [
                    { "key": "X-API-KEY", "value": "{{apiKey}}" },
                    { "key": "Content-Type", "value": "application/json" }
                ],
                "url": { "raw": "{{baseUrl}}/api/partner/users", "host": ["{{baseUrl}}"], "path": ["api", "partner", "users"] },
                "body": {
                    "mode": "raw",
                    "raw": "{\n    \"partnerUserId\": \"{{partnerUserId}}\",\n    \"email\": \"demo.user.001@example.com\",\n    \"firstName\": \"Demo\",\n    \"lastName\": \"Farmer\",\n    \"countryCode\": \"US\"\n}"
                },
                "description": "Creates (or finds) the Launch Pad user for your `partnerUserId`. Idempotent - retries return the same user. `email`, `firstName`, and `lastName` are only required the first time, when the user is actually created."
            }
        },
        {
            "name": "2. Provision company",
            "event": [
                {
                    "listen": "test",
                    "script": {
                        "type": "text/javascript",
                        "exec": [
                            "pm.test('200 OK', () => pm.response.to.have.status(200));",
                            "pm.test('returns a Launch Pad companyId', () => pm.expect(pm.response.json().companyId).to.be.a('string'));"
                        ]
                    }
                }
            ],
            "request": {
                "method": "POST",
                "header": [
                    { "key": "X-API-KEY", "value": "{{apiKey}}" },
                    { "key": "Content-Type", "value": "application/json" }
                ],
                "url": { "raw": "{{baseUrl}}/api/partner/companies", "host": ["{{baseUrl}}"], "path": ["api", "partner", "companies"] },
                "body": {
                    "mode": "raw",
                    "raw": "{\n    \"partnerCompanyId\": \"{{partnerCompanyId}}\",\n    \"name\": \"Demo Customer Co\",\n    \"countryCode\": \"US\"\n}"
                },
                "description": "Creates (or finds) the customer org for your `partnerCompanyId` and grants your API key's user admin access to it. Idempotent on `partnerCompanyId`."
            }
        },
        {
            "name": "3. Grant company access",
            "event": [
                {
                    "listen": "test",
                    "script": {
                        "type": "text/javascript",
                        "exec": [
                            "pm.test('200 OK', () => pm.response.to.have.status(200));",
                            "pm.test('access granted', () => pm.expect(pm.response.json().granted).to.eql(true));"
                        ]
                    }
                }
            ],
            "request": {
                "method": "POST",
                "header": [
                    { "key": "X-API-KEY", "value": "{{apiKey}}" },
                    { "key": "Content-Type", "value": "application/json" }
                ],
                "url": { "raw": "{{baseUrl}}/api/partner/company-accesses", "host": ["{{baseUrl}}"], "path": ["api", "partner", "company-accesses"] },
                "body": {
                    "mode": "raw",
                    "raw": "{\n    \"partnerUserId\": \"{{partnerUserId}}\",\n    \"partnerCompanyId\": \"{{partnerCompanyId}}\"\n}"
                },
                "description": "Links the user to the org - both referenced by your own ids. Idempotent: granting twice is a no-op."
            }
        },
        {
            "name": "4. Create launch",
            "event": [
                {
                    "listen": "test",
                    "script": {
                        "type": "text/javascript",
                        "exec": [
                            "pm.test('200 OK', () => pm.response.to.have.status(200));",
                            "const res = pm.response.json();",
                            "pm.collectionVariables.set('launchCode', res.code);",
                            "pm.collectionVariables.set('launchPadUserId', res.userId);",
                            "const rt = res.returnUrl ? `&returnUrl=${encodeURIComponent(res.returnUrl)}` : '';",
                            "const launchUrl = res.launchUrl || `${pm.collectionVariables.get('baseUrl')}/launch?code=${encodeURIComponent(res.code)}&userId=${res.userId}${rt}`;",
                            "pm.collectionVariables.set('launchUrl', launchUrl);",
                            "console.log(`Open in a browser to sign in:\\n${launchUrl}`);"
                        ]
                    }
                }
            ],
            "request": {
                "method": "POST",
                "header": [
                    { "key": "X-API-KEY", "value": "{{apiKey}}" },
                    { "key": "Content-Type", "value": "application/json" }
                ],
                "url": { "raw": "{{baseUrl}}/api/partner/launch", "host": ["{{baseUrl}}"], "path": ["api", "partner", "launch"] },
                "body": {
                    "mode": "raw",
                    "raw": "{\n    \"userId\": \"{{partnerUserId}}\",\n    \"returnUrl\": \"/dashboard\"\n}"
                },
                "description": "Issues a one-time launch code (10-minute TTL, single-use) for your `partnerUserId`. The `userId` field here is YOUR partner id; the response's `userId` is the resolved Launch Pad guid.\n\nThe test script saves `launchCode`, `launchPadUserId`, and the full `launchUrl` as collection variables (and logs the URL to the console). It uses the `launchUrl` returned by the API when present, and otherwise builds it from `baseUrl` + the code/user id. Open `launchUrl` in a browser and Launch Pad signs the user in automatically and forwards them to `returnUrl`."
            }
        },
        {
            "name": "5. Exchange launch code",
            "event": [
                {
                    "listen": "test",
                    "script": {
                        "type": "text/javascript",
                        "exec": [
                            "pm.test('200 OK', () => pm.response.to.have.status(200));",
                            "pm.test('returns a session token', () => pm.expect(pm.response.json().token).to.be.an('object'));",
                            "console.log('Session issued. Note: the code is now consumed - running this again returns 400 LaunchCodeInvalid.');"
                        ]
                    }
                }
            ],
            "request": {
                "method": "POST",
                "header": [
                    { "key": "Content-Type", "value": "application/json" }
                ],
                "url": { "raw": "{{baseUrl}}/api/partner/launch/exchange", "host": ["{{baseUrl}}"], "path": ["api", "partner", "launch", "exchange"] },
                "body": {
                    "mode": "raw",
                    "raw": "{\n    \"code\": \"{{launchCode}}\",\n    \"userId\": \"{{launchPadUserId}}\"\n}"
                },
                "description": "Exchanges the one-time code for a Launch Pad session (JWT + refresh token). No API key: the code itself is the credential (this is the call Launch Pad's own `/launch` page makes on arrival). Single-use - a second call with the same code returns `400 LaunchCodeInvalid`."
            }
        }
    ]
}
