{
  "info": {
    "_postman_id": "tcv2-platform-001",
    "name": "Teamcast v2 — Platform Admin APIs",
    "description": "Platform admin API surface. Used by Teamcast super admins AND partner platform admins (e.g. LinkedIn) to manage their customer tenants and provision integrations.\n\n**Auth:** Bearer JWT (`{{jwtToken}}`). Caller must have `platform_admin` role or super admin (`*` permissions).\n\n**Base URL:** `{{baseUrl}}` defaults to `http://localhost:4100/api/v2`.\n\nCovers: tenant invite flow, customer tenant API key management, webhook config management, OAuth client registry, billing console, support inbox, feature flag overrides, and webhook DLQ.",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
  },
  "auth": {
    "type": "bearer",
    "bearer": [
      {
        "key": "token",
        "value": "{{jwtToken}}",
        "type": "string"
      }
    ]
  },
  "variable": [
    {
      "key": "baseUrl",
      "value": "http://localhost:4100/api/v2",
      "type": "string"
    }
  ],
  "item": [
    {
      "item": [
        {
          "event": [
            {
              "script": {
                "type": "text/javascript",
                "exec": [
                  "if (pm.response.code === 200) {",
                  "  const r = pm.response.json();",
                  "  if (r.accessToken) pm.environment.set('jwtToken', r.accessToken);",
                  "  pm.test('Has access token', () => pm.expect(r.accessToken).to.be.a('string'));",
                  "  console.log('Logged in as:', r.user?.email, '| role:', r.user?.role);",
                  "}"
                ]
              },
              "listen": "test"
            }
          ],
          "request": {
            "url": {
              "path": [
                "auth",
                "login"
              ],
              "raw": "{{baseUrl}}/auth/login",
              "host": [
                "{{baseUrl}}"
              ]
            },
            "auth": {
              "type": "noauth"
            },
            "body": {
              "options": {
                "raw": {
                  "language": "json"
                }
              },
              "mode": "raw",
              "raw": "{\\n  \"email\":    \"{{platformAdminEmail}}\",\\n  \"password\": \"{{platformAdminPassword}}\"\\n}"
            },
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "description": "Password login for platform admin accounts. Returns accessToken + user object. Rate-limited."
          },
          "name": "Login (Platform Admin)"
        },
        {
          "request": {
            "header": [],
            "url": {
              "path": [
                "auth",
                "me"
              ],
              "raw": "{{baseUrl}}/auth/me",
              "host": [
                "{{baseUrl}}"
              ]
            },
            "method": "GET",
            "description": "Returns the current platform admin's profile and effective permissions. Use to verify your JWT is valid."
          },
          "name": "Get My Profile"
        }
      ],
      "description": "Login with your platform admin credentials to get a JWT. The Login test script auto-saves jwtToken to the environment — all other folders use it via Bearer auth.",
      "name": "0. Authentication"
    },
    {
      "name": "1. Platform — Tenant Management",
      "description": "Cross-tenant visibility and control for Teamcast staff (support ops, onboarding, impersonation).",
      "item": [
        {
          "name": "List Customer Tenants",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/platform/tenants?page=1&limit=20",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "platform",
                "tenants"
              ],
              "query": [
                {
                  "key": "page",
                  "value": "1"
                },
                {
                  "key": "limit",
                  "value": "20"
                },
                {
                  "key": "search",
                  "value": "",
                  "disabled": true
                }
              ]
            },
            "description": "Returns all customer tenants with user counts and interview usage."
          }
        },
        {
          "name": "Get Tenant Detail",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/platform/tenants/{{platformTenantId}}",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "platform",
                "tenants",
                "{{platformTenantId}}"
              ]
            },
            "description": "Returns full tenant detail: users, roles, interview and job counts."
          }
        },
        {
          "name": "Onboard New Tenant",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"name\":         \"New Customer Corp\",\n  \"slug\":         \"new-customer-corp\",\n  \"adminEmail\":   \"admin@newcustomer.ai\",\n  \"plan_key\":     \"starter\",\n  \"trial_days\":   14\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": {
              "raw": "{{baseUrl}}/platform/tenants",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "platform",
                "tenants"
              ]
            },
            "description": "Creates a new customer tenant and their initial admin user. Sends a welcome/verification email."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "if ([200, 201].includes(pm.response.code)) {",
                  "  const r = pm.response.json();",
                  "  if (r.id || r.tenant?.id) pm.environment.set('platformTenantId', r.id || r.tenant?.id);",
                  "  console.log('platformTenantId:', r.id || r.tenant?.id);",
                  "}"
                ]
              }
            }
          ]
        },
        {
          "name": "Update Tenant (rename / suspend / reactivate)",
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"name\":   \"New Customer Corp (Rebranded)\",\n  \"status\": \"active\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": {
              "raw": "{{baseUrl}}/platform/tenants/{{platformTenantId}}",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "platform",
                "tenants",
                "{{platformTenantId}}"
              ]
            },
            "description": "Valid `status` values: active | suspended. Suspend immediately blocks all logins for the tenant."
          }
        },
        {
          "name": "List Tenant Jobs (support context)",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/platform/tenants/{{platformTenantId}}/jobs",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "platform",
                "tenants",
                "{{platformTenantId}}",
                "jobs"
              ]
            }
          }
        },
        {
          "name": "List Tenant Interviews (support context)",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/platform/tenants/{{platformTenantId}}/interviews?page=1&limit=20",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "platform",
                "tenants",
                "{{platformTenantId}}",
                "interviews"
              ],
              "query": [
                {
                  "key": "page",
                  "value": "1"
                },
                {
                  "key": "limit",
                  "value": "20"
                }
              ]
            }
          }
        },
        {
          "name": "Re-trigger Assessment Scoring (any interview)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": {
              "raw": "{{baseUrl}}/platform/interviews/{{interviewId}}/rescore",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "platform",
                "interviews",
                "{{interviewId}}",
                "rescore"
              ]
            },
            "description": "Re-runs measurement scoring for any interview across any tenant. Returns 202. Use when an interview is stuck on ASSESSMENT_PENDING."
          }
        },
        {
          "name": "Get Cross-Tenant Audit Log",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/platform/audit?page=1&limit=50",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "platform",
                "audit"
              ],
              "query": [
                {
                  "key": "page",
                  "value": "1"
                },
                {
                  "key": "limit",
                  "value": "50"
                },
                {
                  "key": "tenantId",
                  "value": "",
                  "disabled": true
                },
                {
                  "key": "actorId",
                  "value": "",
                  "disabled": true
                },
                {
                  "key": "action",
                  "value": "",
                  "disabled": true
                }
              ]
            },
            "description": "Immutable audit log across all tenants. Filter by tenant, actor, or action type."
          }
        }
      ]
    },
    {
      "name": "2. Platform — Invite Flow",
      "description": "Invite partner platform admins and customer workspace admins via signed one-time email tokens. Accepted invites land on /platform/accept-invite where the user sets their password.",
      "item": [
        {
          "name": "Invite Partner Platform Admin",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"name\":         \"LinkedIn Recruiting\",\n  \"email\":        \"admin@partner.example.com\",\n  \"platformName\": \"LinkedIn Recruiting Platform\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": {
              "raw": "{{baseUrl}}/platform/admins/invite",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "platform",
                "admins",
                "invite"
              ]
            },
            "description": "Creates a new partner platform tenant and sends a one-time setup-link email to the admin. Requires `platform:admins:invite`. The invitee accepts at POST /auth/accept-invite/:token."
          }
        },
        {
          "name": "Invite Customer Workspace Admin",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"name\":            \"Jane Smith\",\n  \"email\":           \"admin@customer.example.com\",\n  \"workspaceName\":   \"Acme Corp\",\n  \"parentPlatformId\": \"{{platformTenantId}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": {
              "raw": "{{baseUrl}}/platform/tenants/invite",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "platform",
                "tenants",
                "invite"
              ]
            },
            "description": "Creates a new customer workspace and sends a one-time setup-link email to the workspace admin. Requires `platform:tenants:manage`. `parentPlatformId` ties the new workspace to the calling platform admin's tenant."
          }
        }
      ]
    },
    {
      "name": "3. Platform — Customer Tenant API Keys",
      "description": "Platform admins provisioning API keys for any customer tenant under their platform. Keys are immediately visible to that tenant's admins at /recruiter/integrations/api-keys.",
      "item": [
        {
          "name": "List API Keys (for customer tenant)",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/admin/tenants/{{platformTenantId}}/api-keys",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "admin",
                "tenants",
                "{{platformTenantId}}",
                "api-keys"
              ]
            },
            "description": "Returns all API keys scoped to the given customer tenant. Requires `apikey:read` permission."
          }
        },
        {
          "name": "Create API Key (for customer tenant)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"name\": \"Liha ATS integration\",\n  \"permissions\": [\n    \"interview:create\",\n    \"interview:read\",\n    \"interview:approve\",\n    \"interview:update\",\n    \"candidate:read\",\n    \"recording:read\",\n    \"webhook:read\"\n  ]\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": {
              "raw": "{{baseUrl}}/admin/tenants/{{platformTenantId}}/api-keys",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "admin",
                "tenants",
                "{{platformTenantId}}",
                "api-keys"
              ]
            },
            "description": "Creates an API key scoped to the customer tenant. The raw key (`key` field) is returned ONCE — store it immediately. `platform:*` permissions are rejected. No escalation guard in admin context."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "if ([200, 201].includes(pm.response.code)) {",
                  "  const r = pm.response.json();",
                  "  if (r.id)  pm.environment.set('apiKeyId', r.id);",
                  "  if (r.key) { pm.environment.set('apiKey', r.key); console.log('Save API key now — shown once:', r.key); }",
                  "}"
                ]
              }
            }
          ]
        },
        {
          "name": "Update API Key (for customer tenant)",
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"name\": \"Liha ATS integration (updated)\",\n  \"permissions\": [\n    \"interview:create\",\n    \"interview:read\"\n  ]\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": {
              "raw": "{{baseUrl}}/admin/tenants/{{platformTenantId}}/api-keys/{{apiKeyId}}",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "admin",
                "tenants",
                "{{platformTenantId}}",
                "api-keys",
                "{{apiKeyId}}"
              ]
            },
            "description": "Updates name and/or permissions on an existing key. Requires `platform:tenants:manage`."
          }
        },
        {
          "name": "Revoke API Key (for customer tenant)",
          "request": {
            "method": "DELETE",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/admin/tenants/{{platformTenantId}}/api-keys/{{apiKeyId}}",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "admin",
                "tenants",
                "{{platformTenantId}}",
                "api-keys",
                "{{apiKeyId}}"
              ]
            },
            "description": "Permanently revokes the key. All in-flight requests using it will immediately start returning 401."
          }
        }
      ]
    },
    {
      "name": "4. Platform — Customer Tenant Webhook Config",
      "description": "Platform admins managing the webhook config for any customer tenant. Config is immediately active for that tenant.",
      "item": [
        {
          "name": "Get Webhook Config (for customer tenant)",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/admin/tenants/{{platformTenantId}}/webhook-config",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "admin",
                "tenants",
                "{{platformTenantId}}",
                "webhook-config"
              ]
            },
            "description": "Returns the tenant's current webhook config, or null if not configured. Requires `webhook:read`."
          }
        },
        {
          "name": "Upsert Webhook Config (for customer tenant)",
          "request": {
            "method": "PUT",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"callback_url\": \"https://customer.example.com/webhooks/teamcast\",\n  \"secret\":       \"a1b2c3d4e5f6...\",\n  \"events\": [\n    \"interview.plan_generated\",\n    \"interview.assessment_ready\",\n    \"interview.completed\"\n  ],\n  \"is_active\": true\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": {
              "raw": "{{baseUrl}}/admin/tenants/{{platformTenantId}}/webhook-config",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "admin",
                "tenants",
                "{{platformTenantId}}",
                "webhook-config"
              ]
            },
            "description": "Creates or replaces the tenant's webhook config. Each save rotates the signing secret — generate a new one before saving and store it. `events: []` subscribes to all events. Requires `webhook:update`."
          }
        },
        {
          "name": "Delete Webhook Config (for customer tenant)",
          "request": {
            "method": "DELETE",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/admin/tenants/{{platformTenantId}}/webhook-config",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "admin",
                "tenants",
                "{{platformTenantId}}",
                "webhook-config"
              ]
            },
            "description": "Removes the tenant's webhook config. Deliveries stop immediately. Requires `webhook:update`."
          }
        }
      ]
    },
    {
      "name": "9. Webhook DLQ",
      "description": "Dead Letter Queue for failed webhook deliveries. Retry or investigate undelivered events.",
      "item": [
        {
          "name": "List Dead Letter Queue",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/admin/webhook-dlq?tenantId={{platformTenantId}}&page=1&limit=20",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "admin",
                "webhook-dlq"
              ],
              "query": [
                {
                  "key": "tenantId",
                  "value": "{{platformTenantId}}"
                },
                {
                  "key": "page",
                  "value": "1"
                },
                {
                  "key": "limit",
                  "value": "20"
                }
              ]
            },
            "description": "Returns dead-lettered webhook deliveries for a given tenant. Includes error message and retry count."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "if (pm.response.code === 200) {",
                  "  const r = pm.response.json();",
                  "  const first = r.data?.[0] || r.items?.[0] || r[0];",
                  "  if (first?.id) pm.environment.set('dlqEntryId', first.id);",
                  "}"
                ]
              }
            }
          ]
        },
        {
          "name": "Retry DLQ Entry",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": {
              "raw": "{{baseUrl}}/admin/webhook-dlq/{{dlqEntryId}}/retry",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "admin",
                "webhook-dlq",
                "{{dlqEntryId}}",
                "retry"
              ]
            },
            "description": "Re-attempts delivery of a dead-lettered webhook event immediately."
          }
        },
        {
          "name": "Fire Synthetic Test Delivery",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"event\": \"interview.plan_generated\",\n  \"payload\": {\n    \"run_id\": \"test-run-001\",\n    \"state\":  \"PLAN_PENDING\"\n  }\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": {
              "raw": "{{baseUrl}}/admin/tenants/{{platformTenantId}}/webhook-test",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "admin",
                "tenants",
                "{{platformTenantId}}",
                "webhook-test"
              ]
            },
            "description": "Sends a synthetic webhook delivery to the tenant's configured callback URL. Used to verify endpoint reachability."
          }
        }
      ]
    },
    {
      "name": "10. Health",
      "description": "Liveness and readiness probes. Not under /api/v2 prefix.",
      "auth": {
        "type": "noauth"
      },
      "item": [
        {
          "name": "Liveness",
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "GET",
            "header": [],
            "url": {
              "raw": "http://localhost:4100/health/live",
              "host": [
                "http://localhost:4100"
              ],
              "path": [
                "health",
                "live"
              ]
            }
          }
        },
        {
          "name": "Readiness",
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "GET",
            "header": [],
            "url": {
              "raw": "http://localhost:4100/health/ready",
              "host": [
                "http://localhost:4100"
              ],
              "path": [
                "health",
                "ready"
              ]
            }
          }
        }
      ]
    }
  ]
}
