{
  "info": {
    "_postman_id": "teamcast-interview-integration-002",
    "name": "Teamcast Maya - Interview Integration APIs",
    "description": "External REST Integration APIs for Teamcast Maya AI Interview Platform.\n\nUse these endpoints to integrate your ATS, HRIS, or recruitment platform with Teamcast Maya.\n\n**Authentication:** API Key via `X-API-Key` header.\n\nYour API key is issued by your Platform Admin via the Platform Admin portal (POST /platform-admin/api-keys). Pass it on every request as:\n```\nX-API-Key: your_api_key_here\nX-Tenant-ID: your_tenant_uuid\n```\n\n**Webhook events** are delivered to the `callbackUrl` you supply when creating an interview. Subscribe to them to drive your workflow without polling.\n\n**Lifecycle:**\n```\nPOST /integration/interviews\n  → INFO_NEEDED (if data incomplete) → PATCH /integration/interviews/:runId/info\n  → VALIDATING_SKILLS → GENERATING_PLAN\n  → PENDING (plan ready) → POST /plan/approve\n  → APPROVED → SCHEDULED → IN_PROGRESS → COMPLETED\n  → ASSESSMENT_PENDING → POST /assessment/approve\n  → ASSESSMENT_APPROVED\n```",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
  },
  "auth": {
    "type": "apikey",
    "apikey": [
      { "key": "key", "value": "X-API-Key", "type": "string" },
      { "key": "value", "value": "{{apiKey}}", "type": "string" },
      { "key": "in", "value": "header", "type": "string" }
    ]
  },
  "variable": [
    {
      "key": "baseUrl",
      "value": "http://localhost:3009/api/v1"
    }
  ],
  "item": [
    {
      "name": "Interview Lifecycle",
      "description": "Core interview flow. Use runId returned from step 1 for all subsequent calls.",
      "item": [
        {
          "name": "1. Submit Interview Request",
          "event": [
            {
              "listen": "test",
              "script": {
                "exec": [
                  "if (pm.response.code === 201 || pm.response.code === 200) {",
                  "  const json = pm.response.json();",
                  "  pm.environment.set('runId', json.runId);",
                  "  if (json.interviewId) pm.environment.set('interviewId', json.interviewId);",
                  "  pm.test('Interview created', () => pm.expect(json.runId).to.be.a('string'));",
                  "  pm.test('Has state', () => pm.expect(json.state).to.be.a('string'));",
                  "  console.log('State:', json.state);",
                  "  if (json.state === 'INFO_NEEDED') console.log('Missing fields:', JSON.stringify(json.missingFields));",
                  "}"
                ],
                "type": "text/javascript"
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "X-Tenant-ID", "value": "{{tenantId}}", "description": "Your tenant UUID" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"candidateRef\": \"ats_candidate_12345\",\n  \"candidateName\": \"Jane Smith\",\n  \"candidateEmail\": \"jane.smith@example.com\",\n  \"position\": \"Senior Frontend Engineer\",\n  \"level\": \"SENIOR\",\n  \"qualifications\": [\n    \"5+ years production React/TypeScript experience\",\n    \"Experience with design systems and component libraries\",\n    \"Performance optimization and Core Web Vitals\"\n  ],\n  \"skills\": [\"React\", \"TypeScript\", \"CSS\", \"Testing\"],\n  \"jobDescription\": \"We are looking for a senior frontend engineer to lead our design system initiative. You will architect reusable component libraries, optimize bundle sizes, and mentor junior developers. Experience with React Server Components and Next.js is a plus.\",\n  \"companyName\": \"Acme Corp\",\n  \"companyDescription\": \"Enterprise SaaS platform for HR automation\",\n  \"callbackUrl\": \"https://your-ats.example.com/webhooks/teamcast\",\n  \"externalId\": \"ats_job_789_candidate_12345\"\n}"
            },
            "url": {
              "raw": "{{baseUrl}}/integration/interviews",
              "host": ["{{baseUrl}}"],
              "path": ["integration", "interviews"]
            },
            "description": "Submit a new interview request.\n\n**Returns `runId` immediately** — use this for all subsequent calls.\n\n**Possible states in response:**\n- `INFO_NEEDED` — critical fields missing. Check `missingFields` array, then call PATCH /info.\n- `VALIDATING_SKILLS` — all data present. AI plan generation started.\n\n**`externalId`** (optional) — your ATS job/application ID. Makes creation idempotent: retries return the existing interview instead of creating a duplicate.\n\n**`callbackUrl`** — where we POST webhook events. Required to receive async state updates."
          },
          "response": []
        },
        {
          "name": "2. Get Interview Status",
          "request": {
            "method": "GET",
            "header": [
              { "key": "X-Tenant-ID", "value": "{{tenantId}}" }
            ],
            "url": {
              "raw": "{{baseUrl}}/integration/interviews/{{runId}}",
              "host": ["{{baseUrl}}"],
              "path": ["integration", "interviews", "{{runId}}"]
            },
            "description": "Get current state, plan details, and assessment for an interview.\n\nUse this to poll for state changes or fetch the full plan/assessment when available.\n\n**Lifecycle states (in order):**\nINFO_NEEDED → VALIDATING_SKILLS → GENERATING_PLAN → PENDING → APPROVED → SCHEDULED → IN_PROGRESS → COMPLETED → ASSESSMENT_PENDING → ASSESSMENT_APPROVED"
          },
          "response": []
        },
        {
          "name": "3. Supply Missing Info (INFO_NEEDED)",
          "request": {
            "method": "PATCH",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "X-Tenant-ID", "value": "{{tenantId}}" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"candidateName\": \"Jane Smith\",\n  \"candidateEmail\": \"jane.smith@example.com\",\n  \"jobDescription\": \"Senior frontend engineer position requiring 5+ years React experience, design system expertise, and performance optimization skills. The role involves mentoring junior developers and driving architectural decisions.\",\n  \"skills\": [\"React\", \"TypeScript\", \"CSS\", \"Testing\", \"Next.js\"],\n  \"level\": \"SENIOR\"\n}"
            },
            "url": {
              "raw": "{{baseUrl}}/integration/interviews/{{runId}}/info",
              "host": ["{{baseUrl}}"],
              "path": ["integration", "interviews", "{{runId}}", "info"]
            },
            "description": "Supply missing fields when the interview is in `INFO_NEEDED` state.\n\nTriggered by webhook `interview.info_needed`. Only send the fields listed as missing — existing fields are preserved.\n\n**After this call:**\n- If all critical fields are now present → transitions to `VALIDATING_SKILLS`, fires `interview.info_completed` webhook.\n- If fields still missing → returns remaining `missingFields`, stays in `INFO_NEEDED`.\n\n**Critical fields:** `candidateName`, `candidateEmail`, `position`\n**High-priority fields:** `jobDescription` (min 50 chars), `skills`, `level`"
          },
          "response": []
        },
        {
          "name": "4. Approve Plan (PENDING)",
          "event": [
            {
              "listen": "test",
              "script": {
                "exec": [
                  "if (pm.response.code === 200) {",
                  "  const json = pm.response.json();",
                  "  if (json.interviewLink) {",
                  "    pm.environment.set('interviewLink', json.interviewLink);",
                  "    console.log('Interview link:', json.interviewLink);",
                  "  }",
                  "}"
                ],
                "type": "text/javascript"
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "X-Tenant-ID", "value": "{{tenantId}}" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"comments\": \"Plan covers all required skills. Proceed.\",\n  \"actorId\": \"recruiter_user_id\"\n}"
            },
            "url": {
              "raw": "{{baseUrl}}/integration/interviews/{{runId}}/plan/approve",
              "host": ["{{baseUrl}}"],
              "path": ["integration", "interviews", "{{runId}}", "plan", "approve"]
            },
            "description": "Approve the AI-generated interview plan.\n\nTriggered after receiving webhook `interview.plan_generated`.\n\n**Response includes:**\n- `interviewLink` — URL to send to the candidate\n- `inmailDraft` — pre-filled LinkedIn InMail subject + body with the interview link\n\nFires webhook `interview.approved`."
          },
          "response": []
        },
        {
          "name": "4a. Reject Plan (PENDING)",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "X-Tenant-ID", "value": "{{tenantId}}" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"reason\": \"Plan does not cover enough system design topics. Please add 3-4 distributed systems questions.\",\n  \"actorId\": \"recruiter_user_id\"\n}"
            },
            "url": {
              "raw": "{{baseUrl}}/integration/interviews/{{runId}}/plan/reject",
              "host": ["{{baseUrl}}"],
              "path": ["integration", "interviews", "{{runId}}", "plan", "reject"]
            },
            "description": "Reject the plan. The AI agent uses your `reason` as feedback and regenerates the plan.\n\nFires webhook `interview.rejected`, then `interview.plan_generated` when the new plan is ready."
          },
          "response": []
        },
        {
          "name": "4b. Revise Plan (PENDING)",
          "request": {
            "method": "PATCH",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "X-Tenant-ID", "value": "{{tenantId}}" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"comments\": \"Add more questions on distributed systems and reduce CSS-specific questions to 1-2\",\n  \"actorId\": \"recruiter_user_id\"\n}"
            },
            "url": {
              "raw": "{{baseUrl}}/integration/interviews/{{runId}}/plan/revise",
              "host": ["{{baseUrl}}"],
              "path": ["integration", "interviews", "{{runId}}", "plan", "revise"]
            },
            "description": "Request targeted revisions to the plan without a full rejection.\n\nUse this for minor changes (e.g. 'add 2 more system design questions'). The AI uses `comments` as context when regenerating.\n\nOptionally include an updated `jobDescription` to replace the original.\n\nFires webhook `interview.modification_requested`."
          },
          "response": []
        },
        {
          "name": "5. Approve Assessment (ASSESSMENT_PENDING)",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "X-Tenant-ID", "value": "{{tenantId}}" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"approved\": true,\n  \"annotations\": \"Assessment accurately reflects candidate performance.\",\n  \"actorId\": \"recruiter_user_id\"\n}"
            },
            "url": {
              "raw": "{{baseUrl}}/integration/interviews/{{runId}}/assessment/approve",
              "host": ["{{baseUrl}}"],
              "path": ["integration", "interviews", "{{runId}}", "assessment", "approve"]
            },
            "description": "Approve the AI-generated assessment report.\n\nTriggered after receiving webhook `interview.assessment_pending`.\n\nFires webhook `interview.assessment_completed` with the full assessment report."
          },
          "response": []
        },
        {
          "name": "5a. Reject Assessment",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "X-Tenant-ID", "value": "{{tenantId}}" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"reason\": \"Assessment does not match observed candidate performance in the interview\",\n  \"actorId\": \"recruiter_user_id\"\n}"
            },
            "url": {
              "raw": "{{baseUrl}}/integration/interviews/{{runId}}/assessment/reject",
              "host": ["{{baseUrl}}"],
              "path": ["integration", "interviews", "{{runId}}", "assessment", "reject"]
            },
            "description": "Reject the AI assessment. The interview is marked as CANCELLED and the assessment is suppressed."
          },
          "response": []
        },
        {
          "name": "5b. Chat About Assessment",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "X-Tenant-ID", "value": "{{tenantId}}" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"message\": \"Why did the candidate score low on system design?\",\n  \"history\": []\n}"
            },
            "url": {
              "raw": "{{baseUrl}}/integration/interviews/{{runId}}/assessment/chat",
              "host": ["{{baseUrl}}"],
              "path": ["integration", "interviews", "{{runId}}", "assessment", "chat"]
            },
            "description": "Ask the AI free-form questions about the assessment.\n\nPass `history` (array of prior `{role, content}` pairs) to maintain conversational context across multiple questions."
          },
          "response": []
        },
        {
          "name": "Get Candidate Rankings",
          "request": {
            "method": "GET",
            "header": [
              { "key": "X-Tenant-ID", "value": "{{tenantId}}" }
            ],
            "url": {
              "raw": "{{baseUrl}}/integration/rankings?position=Senior Engineer&level=SENIOR",
              "host": ["{{baseUrl}}"],
              "path": ["integration", "rankings"],
              "query": [
                { "key": "position", "value": "Senior Engineer", "description": "Filter by position (case-insensitive substring match)" },
                { "key": "level", "value": "SENIOR", "description": "Filter by level: JUNIOR | MID | SENIOR | LEAD" }
              ]
            },
            "description": "Get all candidates with completed assessments ranked by overall score (descending).\n\nFilter by `position` and/or `level` to compare candidates for a specific role."
          },
          "response": []
        },
        {
          "name": "Cancel Interview",
          "request": {
            "method": "DELETE",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "X-Tenant-ID", "value": "{{tenantId}}" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"reason\": \"Candidate withdrew from process\"\n}"
            },
            "url": {
              "raw": "{{baseUrl}}/integration/interviews/{{runId}}",
              "host": ["{{baseUrl}}"],
              "path": ["integration", "interviews", "{{runId}}"]
            },
            "description": "Cancel an active interview (soft delete). Cannot cancel interviews in COMPLETED, CANCELLED, or ASSESSMENT_APPROVED state."
          },
          "response": []
        },
        {
          "name": "Purge Candidate PII (GDPR)",
          "request": {
            "method": "DELETE",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "X-Tenant-ID", "value": "{{tenantId}}" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"actorId\": \"gdpr_request_handler\"\n}"
            },
            "url": {
              "raw": "{{baseUrl}}/integration/interviews/{{runId}}/candidate-data",
              "host": ["{{baseUrl}}"],
              "path": ["integration", "interviews", "{{runId}}", "candidate-data"]
            },
            "description": "Irreversibly wipe all candidate PII from an interview record.\n\n**Wiped:** `candidateName`, `candidateEmail`, `candidateRef`, `candidateProfile`, `candidateResume`, OTP records, access tokens.\n\n**Preserved:** assessment scores, qualification results, workflow state history (audit compliance).\n\nCreates an immutable `CandidatePurgeLog` entry. **Cannot be undone.**"
          },
          "response": []
        }
      ]
    },
    {
      "name": "JSON-RPC 2.0 (A2A Protocol)",
      "description": "Use this if your system implements the A2A protocol spec. For standard REST integrations, use the endpoints above instead.",
      "item": [
        {
          "name": "interview.create",
          "event": [
            {
              "listen": "test",
              "script": {
                "exec": [
                  "if (pm.response.code === 200) {",
                  "  const json = pm.response.json();",
                  "  if (json.result && json.result.runId) {",
                  "    pm.environment.set('runId', json.result.runId);",
                  "    console.log('runId saved:', json.result.runId);",
                  "  }",
                  "}"
                ],
                "type": "text/javascript"
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "X-Tenant-ID", "value": "{{tenantId}}" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"jsonrpc\": \"2.0\",\n  \"method\": \"interview.create\",\n  \"params\": {\n    \"candidateRef\": \"ats_candidate_67890\",\n    \"candidateName\": \"Bob Johnson\",\n    \"candidateEmail\": \"bob@example.com\",\n    \"position\": \"Backend Engineer\",\n    \"level\": \"MID\",\n    \"qualifications\": [\n      \"3+ years Node.js or Go experience\",\n      \"Database design and SQL optimization\"\n    ],\n    \"skills\": [\"Node.js\", \"PostgreSQL\", \"Docker\"],\n    \"jobDescription\": \"Backend engineer role handling high-throughput data pipelines and RESTful API design. Must be comfortable with distributed systems and containerized deployments.\",\n    \"callbackUrl\": \"https://your-ats.example.com/webhooks/teamcast\"\n  },\n  \"id\": 1\n}"
            },
            "url": {
              "raw": "{{baseUrl}}/a2a/task",
              "host": ["{{baseUrl}}"],
              "path": ["a2a", "task"]
            }
          },
          "response": []
        },
        {
          "name": "interview.status",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "X-Tenant-ID", "value": "{{tenantId}}" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"jsonrpc\": \"2.0\",\n  \"method\": \"interview.status\",\n  \"params\": {\n    \"runId\": \"{{runId}}\"\n  },\n  \"id\": 2\n}"
            },
            "url": {
              "raw": "{{baseUrl}}/a2a/task",
              "host": ["{{baseUrl}}"],
              "path": ["a2a", "task"]
            }
          },
          "response": []
        },
        {
          "name": "interview.complete-info",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "X-Tenant-ID", "value": "{{tenantId}}" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"jsonrpc\": \"2.0\",\n  \"method\": \"interview.complete-info\",\n  \"params\": {\n    \"runId\": \"{{runId}}\",\n    \"candidateName\": \"Bob Johnson\",\n    \"candidateEmail\": \"bob@example.com\",\n    \"jobDescription\": \"Backend engineer role requiring Node.js expertise and database design skills in a high-throughput environment.\"\n  },\n  \"id\": 3\n}"
            },
            "url": {
              "raw": "{{baseUrl}}/a2a/task",
              "host": ["{{baseUrl}}"],
              "path": ["a2a", "task"]
            }
          },
          "response": []
        },
        {
          "name": "interview.approve",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "X-Tenant-ID", "value": "{{tenantId}}" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"jsonrpc\": \"2.0\",\n  \"method\": \"interview.approve\",\n  \"params\": {\n    \"runId\": \"{{runId}}\",\n    \"approved\": true,\n    \"comments\": \"Plan approved\"\n  },\n  \"id\": 4\n}"
            },
            "url": {
              "raw": "{{baseUrl}}/a2a/task",
              "host": ["{{baseUrl}}"],
              "path": ["a2a", "task"]
            }
          },
          "response": []
        },
        {
          "name": "interview.modify",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "X-Tenant-ID", "value": "{{tenantId}}" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"jsonrpc\": \"2.0\",\n  \"method\": \"interview.modify\",\n  \"params\": {\n    \"runId\": \"{{runId}}\",\n    \"comments\": \"Add more system design questions about distributed caching\"\n  },\n  \"id\": 5\n}"
            },
            "url": {
              "raw": "{{baseUrl}}/a2a/task",
              "host": ["{{baseUrl}}"],
              "path": ["a2a", "task"]
            }
          },
          "response": []
        },
        {
          "name": "assessment.approve",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "X-Tenant-ID", "value": "{{tenantId}}" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"jsonrpc\": \"2.0\",\n  \"method\": \"assessment.approve\",\n  \"params\": {\n    \"runId\": \"{{runId}}\",\n    \"approved\": true,\n    \"annotations\": \"Assessment looks accurate\"\n  },\n  \"id\": 6\n}"
            },
            "url": {
              "raw": "{{baseUrl}}/a2a/task",
              "host": ["{{baseUrl}}"],
              "path": ["a2a", "task"]
            }
          },
          "response": []
        },
        {
          "name": "assessment.chat",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "X-Tenant-ID", "value": "{{tenantId}}" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"jsonrpc\": \"2.0\",\n  \"method\": \"assessment.chat\",\n  \"params\": {\n    \"runId\": \"{{runId}}\",\n    \"message\": \"What areas did the candidate excel in?\",\n    \"history\": []\n  },\n  \"id\": 7\n}"
            },
            "url": {
              "raw": "{{baseUrl}}/a2a/task",
              "host": ["{{baseUrl}}"],
              "path": ["a2a", "task"]
            }
          },
          "response": []
        },
        {
          "name": "interview.rankings",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "X-Tenant-ID", "value": "{{tenantId}}" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"jsonrpc\": \"2.0\",\n  \"method\": \"interview.rankings\",\n  \"params\": {\n    \"position\": \"Senior Engineer\",\n    \"level\": \"SENIOR\"\n  },\n  \"id\": 8\n}"
            },
            "url": {
              "raw": "{{baseUrl}}/a2a/task",
              "host": ["{{baseUrl}}"],
              "path": ["a2a", "task"]
            }
          },
          "response": []
        }
      ]
    },
    {
      "name": "Candidate Access (Token-Based, No Auth)",
      "description": "Candidate-facing endpoints. No API key required — they use the opaque candidate token embedded in the interview link.",
      "item": [
        {
          "name": "Get Interview Info (pre-login)",
          "request": {
            "auth": { "type": "noauth" },
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/candidate-access/{{candidateToken}}/info",
              "host": ["{{baseUrl}}"],
              "path": ["candidate-access", "{{candidateToken}}", "info"]
            },
            "description": "Returns candidate name, position, and masked email for the join page. No authentication required."
          },
          "response": []
        },
        {
          "name": "Request OTP",
          "request": {
            "auth": { "type": "noauth" },
            "method": "POST",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/candidate-access/{{candidateToken}}/request-otp",
              "host": ["{{baseUrl}}"],
              "path": ["candidate-access", "{{candidateToken}}", "request-otp"]
            },
            "description": "Send a 6-digit OTP to the candidate email. Rate limited to 5 requests per 10 minutes."
          },
          "response": []
        },
        {
          "name": "Verify OTP",
          "event": [
            {
              "listen": "test",
              "script": {
                "exec": [
                  "if (pm.response.code === 200 || pm.response.code === 201) {",
                  "  const json = pm.response.json();",
                  "  if (json.sessionToken) {",
                  "    pm.environment.set('sessionToken', json.sessionToken);",
                  "    console.log('Session token saved');",
                  "  }",
                  "}"
                ],
                "type": "text/javascript"
              }
            }
          ],
          "request": {
            "auth": { "type": "noauth" },
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"otp\": \"123456\"\n}"
            },
            "url": {
              "raw": "{{baseUrl}}/candidate-access/{{candidateToken}}/verify-otp",
              "host": ["{{baseUrl}}"],
              "path": ["candidate-access", "{{candidateToken}}", "verify-otp"]
            },
            "description": "Verify OTP and receive a sessionToken. Pass this as `x-session-token` header when starting the interview."
          },
          "response": []
        },
        {
          "name": "Start Interview (after device check)",
          "event": [
            {
              "listen": "test",
              "script": {
                "exec": [
                  "if (pm.response.code === 200) {",
                  "  const json = pm.response.json();",
                  "  if (json.sessionId) pm.environment.set('sessionId', json.sessionId);",
                  "  pm.test('Has sessionId', () => pm.expect(json.sessionId).to.be.a('string'));",
                  "  pm.test('Has wsToken', () => pm.expect(json.wsToken).to.be.a('string'));",
                  "  console.log('Session ID:', json.sessionId);",
                  "  console.log('Duration (mins):', json.durationMinutes);",
                  "}"
                ],
                "type": "text/javascript"
              }
            }
          ],
          "request": {
            "auth": { "type": "noauth" },
            "method": "POST",
            "header": [
              { "key": "x-session-token", "value": "{{sessionToken}}", "description": "Session token from Verify OTP" }
            ],
            "url": {
              "raw": "{{baseUrl}}/candidate-access/{{candidateToken}}/start",
              "host": ["{{baseUrl}}"],
              "path": ["candidate-access", "{{candidateToken}}", "start"]
            },
            "description": "Start the interview session after the candidate completes the device check.\n\n**Response fields:**\n- `sessionId` — use for greet, end, and log endpoints\n- `wsToken` — short-lived token to authenticate the WebSocket connection at the Vert.x edge\n- `wsTokenExpiry` — Unix timestamp when wsToken expires\n- `durationMinutes` — total interview duration\n- `tenantId` — tenant context"
          },
          "response": []
        },
        {
          "name": "Trigger AI Greeting",
          "request": {
            "auth": { "type": "noauth" },
            "method": "POST",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/candidate-access/session/{{sessionId}}/greet",
              "host": ["{{baseUrl}}"],
              "path": ["candidate-access", "session", "{{sessionId}}", "greet"]
            },
            "description": "Trigger the AI interviewer greeting after the WebSocket connection is established.\n\nCall this once the candidate's browser has successfully connected to the WebSocket audio channel. No body required."
          },
          "response": []
        },
        {
          "name": "End Interview",
          "request": {
            "auth": { "type": "noauth" },
            "method": "POST",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/candidate-access/session/{{sessionId}}/end",
              "host": ["{{baseUrl}}"],
              "path": ["candidate-access", "session", "{{sessionId}}", "end"]
            },
            "description": "End the interview session and trigger the AI assessor. Interview must be in IN_PROGRESS state."
          },
          "response": []
        },
        {
          "name": "Log Client Event (proctoring)",
          "request": {
            "auth": { "type": "noauth" },
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"event\": \"tab_switch\",\n  \"level\": \"warn\",\n  \"detail\": \"Candidate switched browser tabs at 00:03:45\"\n}"
            },
            "url": {
              "raw": "{{baseUrl}}/candidate-access/session/{{sessionId}}/log",
              "host": ["{{baseUrl}}"],
              "path": ["candidate-access", "session", "{{sessionId}}", "log"]
            },
            "description": "Log a proctoring lifecycle event for audit purposes. Returns 204 (no body).\n\n**Fields:**\n- `event` (required) — event name, e.g. `tab_switch`, `ws_connected`, `camera_lost` (max 100 chars)\n- `level` (required) — `log` | `warn` | `error`\n- `detail` (optional) — additional context (max 500 chars)"
          },
          "response": []
        }
      ]
    },
    {
      "name": "Agent Discovery (Public)",
      "item": [
        {
          "name": "Get Agent Card",
          "request": {
            "auth": { "type": "noauth" },
            "method": "GET",
            "header": [],
            "url": {
              "raw": "http://localhost:3009/.well-known/agent.json",
              "protocol": "http",
              "host": ["localhost"],
              "port": "3009",
              "path": [".well-known", "agent.json"]
            },
            "description": "Public A2A agent card discovery endpoint. No authentication required. Returns supported methods, authentication schemes, and capabilities."
          },
          "response": []
        }
      ]
    }
  ]
}
