{
  "info": {
    "_postman_id": "transcrimed-oauth2-api-v2",
    "name": "TranscriMed API",
    "description": "TranscriMed API requests for Postman and newman.\n\n- **Integration key (B2B)**: the company-account flow (exchange the key, generate, poll, look up, correct), in order and runnable end to end. Start here if your company integrates with an integration key.\n- **Integration key (B2B): error examples**: credential-free requests showing the error shapes.\n- The **OAuth2** folders cover partner apps acting for a TranscriMed user; their requests use the collection-level token `{{access_token}}`.\n- **Jobs API (legacy)** targets `/api/v1/jobs`, which is deprecated for generation and corrections: poll `/api/v1/pipeline/jobs/{jobId}` instead.\n\nEnvironments live in `environments/`; see the README next to this file.",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
    "version": "2.1.0"
  },
  "auth": {
    "type": "bearer",
    "bearer": [
      {
        "key": "token",
        "value": "{{access_token}}",
        "type": "string"
      }
    ]
  },
  "variable": [
    {
      "key": "base_url",
      "value": "{{base_url}}",
      "type": "string",
      "description": "API base URL - configured per environment"
    },
    {
      "key": "oauth_base_url",
      "value": "{{oauth_base_url}}",
      "type": "string",
      "description": "OAuth2 proxy server URL - configured per environment"
    },
    {
      "key": "client_id",
      "value": "{{client_id}}",
      "type": "string",
      "description": "OAuth2 client ID - configured per environment"
    },
    {
      "key": "client_secret",
      "value": "{{client_secret}}",
      "type": "string",
      "description": "OAuth2 client secret - configured per environment"
    },
    {
      "key": "redirect_uri",
      "value": "{{redirect_uri}}",
      "type": "string",
      "description": "OAuth2 redirect URI - configured per environment"
    },
    {
      "key": "authorization_code",
      "value": "",
      "type": "string",
      "description": "Authorization code from OAuth2 flow"
    },
    {
      "key": "access_token",
      "value": "",
      "type": "string",
      "description": "JWT access token for API authentication"
    },
    {
      "key": "refresh_token",
      "value": "",
      "type": "string",
      "description": "Refresh token for obtaining new access tokens"
    },
    {
      "key": "token_type",
      "value": "Bearer",
      "type": "string",
      "description": "Token type (usually Bearer)"
    },
    {
      "key": "expires_in",
      "value": "",
      "type": "string",
      "description": "Token expiration time in seconds"
    },
    {
      "key": "ik_access_token",
      "value": "",
      "type": "string",
      "description": "Access token from the integration-key exchange (set by step 1/2)"
    },
    {
      "key": "ik_refresh_token",
      "value": "",
      "type": "string",
      "description": "Most recent refresh token (set by step 1/2)"
    },
    {
      "key": "template_id",
      "value": "",
      "type": "string",
      "description": "Template sent by steps 4 and 7. Recomputed by step 3 from the current account (never carried over); set template_id_override to choose one."
    },
    {
      "key": "template_id_override",
      "value": "",
      "type": "string",
      "description": "Optional: a template id of the current account to use instead of its first template. Step 3 fails if it is not one of the account's templates."
    },
    {
      "key": "external_reference_id",
      "value": "",
      "type": "string",
      "description": "Your reference for the demo document (set by step 4)"
    },
    {
      "key": "idempotency_key",
      "value": "",
      "type": "string",
      "description": "Idempotency-Key of the current submission (set by each submit step)"
    },
    {
      "key": "request_id",
      "value": "",
      "type": "string",
      "description": "X-Request-Id of the current submission"
    },
    {
      "key": "job_id",
      "value": "",
      "type": "string",
      "description": "Generation job id (set by step 4)"
    },
    {
      "key": "record_id",
      "value": "",
      "type": "string",
      "description": "Document id (set by step 5)"
    },
    {
      "key": "correction_job_id",
      "value": "",
      "type": "string",
      "description": "Correction job id (set by step 8)"
    },
    {
      "key": "corrected_document",
      "value": "",
      "type": "string",
      "description": "Saved corrected document (set by step 9)"
    },
    {
      "key": "poll_attempt",
      "value": "",
      "type": "string",
      "description": "Polling counter"
    },
    {
      "key": "poll_max_attempts",
      "value": "72",
      "type": "string",
      "description": "Maximum polls per job"
    },
    {
      "key": "poll_interval_ms",
      "value": "5000",
      "type": "string",
      "description": "Delay between polls, in milliseconds"
    },
    {
      "key": "correction_audio_base64",
      "value": "",
      "type": "string",
      "description": "Optional: base64 audio for step 11 (voice correction)"
    }
  ],
  "item": [
    {
      "name": "Integration key (B2B)",
      "description": "The company-account (B2B) flow of the [Integration Key guide](https://developers.transcrimed.com.br/integration-keys), in order.\n\nSet `base_url` and `integration_key` in the environment (use the **Sandbox** environment and a sandbox key while integrating), then run this folder with the Collection Runner, or:\n\n```\nnpx newman run transcrimed-api.postman_collection.json \\\n  -e environments/Sandbox.postman_environment.json \\\n  --env-var integration_key=\"$TRANSCRIMED_INTEGRATION_KEY\" \\\n  --folder \"Integration key (B2B)\"\n```\n\nRequests in this folder use the token from step 1 (`ik_access_token`), not the collection-level OAuth2 token.\n\nThe polling steps re-run themselves every `poll_interval_ms` (default 5000) up to `poll_max_attempts` times (default 72, about 6 minutes). The sandbox sleeps when idle, so its first job can stay `queued` for minutes. A single **Send** in the Postman app does not loop: send the poll again until `status` is `completed`.\n\nEach run generates two documents (one async, one sync), which consume credits of the account. The input is a short, invented ICU progress note: never put real patient data in the sandbox.",
      "auth": {
        "type": "bearer",
        "bearer": [
          {
            "key": "token",
            "value": "{{ik_access_token}}",
            "type": "string"
          }
        ]
      },
      "item": [
        {
          "name": "1. Exchange integration key",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "if (!pm.variables.get('integration_key')) {",
                  "  console.warn('integration_key is empty: set it in the environment (it starts with ik_).');",
                  "}"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "",
                  "const goTo = (name) => (pm.execution && typeof pm.execution.setNextRequest === 'function')",
                  "  ? pm.execution.setNextRequest(name)",
                  "  : postman.setNextRequest(name);",
                  "const body = pm.response.json();",
                  "pm.test('200 with a Bearer token valid for 4 hours', () => {",
                  "  pm.response.to.have.status(200);",
                  "  pm.expect(body.success).to.eql(true);",
                  "  pm.expect(body.data.token_type).to.eql('Bearer');",
                  "  pm.expect(body.data.expires_in).to.eql(14400);",
                  "});",
                  "if (pm.response.code === 200) {",
                  "  pm.collectionVariables.set('ik_access_token', body.data.access_token);",
                  "  pm.collectionVariables.set('ik_refresh_token', body.data.refresh_token);",
                  "} else {",
                  "  console.error('Exchange failed: ' + JSON.stringify(body.error));",
                  "  goTo(null); // nothing else in this folder works without a token",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "text"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"integration_key\": \"{{integration_key}}\"\n}"
            },
            "url": {
              "raw": "{{base_url}}/api/v1/auth/exchange-integration-key",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "auth",
                "exchange-integration-key"
              ]
            },
            "description": "Exchanges the integration key for an access token (4 hours) and a refresh token (24 hours). The key is the credential: no Authorization header. Call this from your backend only; never ship the key to devices."
          },
          "response": [
            {
              "name": "200 token issued",
              "originalRequest": {
                "method": "POST",
                "header": [
                  {
                    "key": "Content-Type",
                    "value": "application/json",
                    "type": "text"
                  }
                ],
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"integration_key\": \"ik_...\"\n}"
                },
                "url": {
                  "raw": "{{base_url}}/api/v1/auth/exchange-integration-key",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "api",
                    "v1",
                    "auth",
                    "exchange-integration-key"
                  ]
                }
              },
              "status": "OK",
              "code": 200,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "body": "{\n  \"success\": true,\n  \"data\": {\n    \"access_token\": \"eyJhbGciOiJIUzI1NiIs...\",\n    \"token_type\": \"Bearer\",\n    \"expires_in\": 14400,\n    \"refresh_token\": \"...\",\n    \"scope\": \"medical_records:read medical_records:write ...\"\n  },\n  \"meta\": {\n    \"requestId\": \"...\",\n    \"timestamp\": \"...\"\n  }\n}"
            },
            {
              "name": "401 invalid key",
              "originalRequest": {
                "method": "POST",
                "header": [
                  {
                    "key": "Content-Type",
                    "value": "application/json",
                    "type": "text"
                  }
                ],
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"integration_key\": \"ik_not_a_real_key\"\n}"
                },
                "url": {
                  "raw": "{{base_url}}/api/v1/auth/exchange-integration-key",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "api",
                    "v1",
                    "auth",
                    "exchange-integration-key"
                  ]
                }
              },
              "status": "Unauthorized",
              "code": 401,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "body": "{\n  \"success\": false,\n  \"error\": {\n    \"error\": \"invalid_key\",\n    \"error_description\": \"Integration key not found or invalid\"\n  },\n  \"meta\": {\n    \"requestId\": \"...\",\n    \"timestamp\": \"...\"\n  }\n}"
            }
          ]
        },
        {
          "name": "2. Refresh the token",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const body = pm.response.json();",
                  "const previous = pm.collectionVariables.get('ik_refresh_token');",
                  "pm.test('200 with a new access token and a new refresh token', () => {",
                  "  pm.response.to.have.status(200);",
                  "  pm.expect(body.data.expires_in).to.eql(14400);",
                  "  pm.expect(body.data.refresh_token).to.be.a('string').and.not.eql(previous);",
                  "});",
                  "if (pm.response.code === 200) {",
                  "  // Always keep the most recent refresh token: the previous one no longer works.",
                  "  pm.collectionVariables.set('ik_access_token', body.data.access_token);",
                  "  pm.collectionVariables.set('ik_refresh_token', body.data.refresh_token);",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "text"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"refresh_token\": \"{{ik_refresh_token}}\"\n}"
            },
            "url": {
              "raw": "{{base_url}}/api/v1/auth/refresh",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "auth",
                "refresh"
              ]
            },
            "description": "Renews the access token before it expires. The response carries a new refresh_token: keep the most recent one. If the refresh fails, exchange the key again."
          },
          "response": []
        },
        {
          "name": "3. List templates",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const body = pm.response.json();",
                  "pm.test('200 with the account templates', () => {",
                  "  pm.response.to.have.status(200);",
                  "  pm.expect(body.data.templates).to.be.an('array');",
                  "});",
                  "const templates = (body.data && body.data.templates) || [];",
                  "// template_id is recomputed from THIS account's list on every run, so a value",
                  "// left over from another environment or account is never sent. To choose a",
                  "// template yourself, set template_id_override (it must belong to the account).",
                  "const override = pm.variables.get('template_id_override');",
                  "if (override) {",
                  "  const owned = templates.some((t) => t.id === override);",
                  "  pm.test('template_id_override is a template of this account', () => {",
                  "    pm.expect(owned, \"template_id_override is not in this account's templates\").to.be.true;",
                  "  });",
                  "  pm.collectionVariables.set('template_id', owned ? override : '');",
                  "} else if (templates.length > 0) {",
                  "  pm.collectionVariables.set('template_id', templates[0].id);",
                  "  console.log('template_id set to \"' + templates[0].name + '\"');",
                  "} else {",
                  "  pm.collectionVariables.set('template_id', '');",
                  "  console.warn('The account has no templates: generation will select one automatically or fail with TEMPLATE_UNRESOLVED.');",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/api/v1/templates",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "templates"
              ]
            },
            "description": "Templates are created in the TranscriMed web app. The first one is stored as template_id unless you set template_id yourself (environment or collection variable)."
          },
          "response": []
        },
        {
          "name": "4. Generate a document (async)",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "// A new submission gets a new Idempotency-Key. To RETRY this same submission",
                  "// (timeout, dropped connection, 5xx), resend it WITHOUT re-running this script:",
                  "// the same key returns the original job and never charges a second credit.",
                  "pm.collectionVariables.set('idempotency_key', pm.variables.replaceIn('{{$guid}}'));",
                  "pm.collectionVariables.set('request_id', pm.variables.replaceIn('{{$guid}}'));",
                  "// Your own identifier for this document. Never patient-identifying.",
                  "pm.collectionVariables.set('external_reference_id', 'postman-demo-' + Date.now());"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "",
                  "const goTo = (name) => (pm.execution && typeof pm.execution.setNextRequest === 'function')",
                  "  ? pm.execution.setNextRequest(name)",
                  "  : postman.setNextRequest(name);",
                  "const body = pm.response.json();",
                  "pm.test('202 with a jobId', () => {",
                  "  pm.response.to.have.status(202);",
                  "  pm.expect(body.data.jobId).to.be.a('string').and.not.empty;",
                  "  pm.expect(body.data.status).to.eql('queued');",
                  "  pm.expect(body.data.statusUrl).to.include('/api/v1/pipeline/jobs/' + body.data.jobId);",
                  "});",
                  "pm.test('external_reference_id is echoed', () => {",
                  "  pm.expect(body.data.external_reference_id).to.eql(pm.variables.get('external_reference_id'));",
                  "});",
                  "if (pm.response.code === 202) {",
                  "  pm.collectionVariables.set('job_id', body.data.jobId);",
                  "  pm.collectionVariables.set('poll_attempt', 0);",
                  "} else {",
                  "  console.error('Generation was not accepted: ' + JSON.stringify(body.error));",
                  "  goTo(null);",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "text"
              },
              {
                "key": "Idempotency-Key",
                "value": "{{idempotency_key}}",
                "type": "text",
                "description": "Required for company accounts. New per submission; the same value to retry it."
              },
              {
                "key": "X-Request-Id",
                "value": "{{request_id}}",
                "type": "text",
                "description": "Recommended. Finds the job with GET /pipeline/jobs/by-request/{id}."
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"text\": \"Evolução de UTI, plantão noturno, leito 12. Paciente fictício, 64 anos, em ventilação mecânica em modo PCV, FiO2 de 40% e PEEP de 8. Em uso de noradrenalina a 0,1 micrograma por quilo por minuto, pressão arterial média de 70. Afebril nas últimas 24 horas, diurese de 1.200 mL no período. Mantida sedação com propofol. Plano: reduzir noradrenalina conforme tolerância e realizar teste de respiração espontânea pela manhã.\",\n  \"template_id\": \"{{template_id}}\",\n  \"mode\": \"async\",\n  \"language\": \"pt-BR\",\n  \"external_reference_id\": \"{{external_reference_id}}\",\n  \"metadata\": {\n    \"source\": \"postman-collection\"\n  }\n}"
            },
            "url": {
              "raw": "{{base_url}}/api/v1/medical-records/generate",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "medical-records",
                "generate"
              ]
            },
            "description": "Generates a document from text (send `audio`, base64, instead of `text` for audio). Answers 202 with a jobId at once. The pre-request script sets a fresh Idempotency-Key and external_reference_id."
          },
          "response": [
            {
              "name": "202 accepted",
              "originalRequest": {
                "method": "POST",
                "header": [
                  {
                    "key": "Content-Type",
                    "value": "application/json",
                    "type": "text"
                  },
                  {
                    "key": "Idempotency-Key",
                    "value": "5d2f8c4a-7b1e-4f3a-9c6d-2e8b0a4f1c37"
                  }
                ],
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"text\": \"...\",\n  \"template_id\": \"6f1c...\",\n  \"mode\": \"async\",\n  \"external_reference_id\": \"bed-12-night-2026-10-21\"\n}"
                },
                "url": {
                  "raw": "{{base_url}}/api/v1/medical-records/generate",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "api",
                    "v1",
                    "medical-records",
                    "generate"
                  ]
                }
              },
              "status": "Accepted",
              "code": 202,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "body": "{\n  \"success\": true,\n  \"data\": {\n    \"jobId\": \"49908b9e-0ad1-4c0c-8919-3fa5fb94552d\",\n    \"status\": \"queued\",\n    \"statusUrl\": \"https://api.sandbox.transcrimed.com.br/api/v1/pipeline/jobs/49908b9e-0ad1-4c0c-8919-3fa5fb94552d\",\n    \"eventsUrl\": \"https://api.sandbox.transcrimed.com.br/api/v1/pipeline/jobs/49908b9e-0ad1-4c0c-8919-3fa5fb94552d/events\",\n    \"estimatedCompletionAt\": \"2026-10-21T15:01:00.000Z\",\n    \"external_reference_id\": \"bed-12-night-2026-10-21\"\n  },\n  \"meta\": {\n    \"request_id\": \"...\",\n    \"timestamp\": \"...\"\n  }\n}"
            }
          ]
        },
        {
          "name": "5. Poll the generation job",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "",
                  "const goTo = (name) => (pm.execution && typeof pm.execution.setNextRequest === 'function')",
                  "  ? pm.execution.setNextRequest(name)",
                  "  : postman.setNextRequest(name);",
                  "const job = pm.response.json();",
                  "const maxAttempts = Number(pm.variables.get('poll_max_attempts') || 72);",
                  "const intervalMs = Number(pm.variables.get('poll_interval_ms') || 5000);",
                  "const attempt = Number(pm.collectionVariables.get('poll_attempt') || 0) + 1;",
                  "pm.collectionVariables.set('poll_attempt', attempt);",
                  "",
                  "pm.test('200 with the job', () => {",
                  "  pm.response.to.have.status(200);",
                  "  pm.expect(job.jobId).to.eql(pm.variables.get('job_id'));",
                  "});",
                  "pm.test('external_reference_id comes back in every state', () => {",
                  "  pm.expect(job.external_reference_id).to.eql(pm.variables.get('external_reference_id'));",
                  "});",
                  "",
                  "if (job.status === 'completed') {",
                  "  pm.collectionVariables.set('poll_attempt', 0);",
                  "  pm.test('the document is ready (HTML) and record_id is stored', () => {",
                  "    pm.expect(job.type).to.eql('medical_record_generation');",
                  "    pm.expect(job.result.result.record_id).to.be.a('string').and.not.empty;",
                  "    pm.expect(job.result.result.text.content).to.be.a('string').and.not.empty;",
                  "    pm.expect(job.document.id).to.eql(job.result.result.record_id);",
                  "  });",
                  "  pm.collectionVariables.set('record_id', job.result.result.record_id);",
                  "  console.log('Document ready after ' + attempt + ' poll(s); record_id stored.');",
                  "} else if (job.status === 'failed') {",
                  "  pm.collectionVariables.set('poll_attempt', 0);",
                  "  pm.test('the job completes', () => {",
                  "    pm.expect.fail('job failed: ' + job.errorCode + ': ' + job.error);",
                  "  });",
                  "  goTo(null);",
                  "} else if (attempt < maxAttempts) {",
                  "  // In the Collection Runner / newman this request runs again after the delay.",
                  "  // A single \"Send\" in the Postman app ignores setNextRequest: send it again by hand.",
                  "  console.log('Attempt ' + attempt + '/' + maxAttempts + ': ' + job.status + '; polling again in ' + intervalMs + ' ms');",
                  "  goTo(pm.info.requestName);",
                  "  setTimeout(() => {}, intervalMs);",
                  "} else {",
                  "  pm.collectionVariables.set('poll_attempt', 0);",
                  "  pm.test('the job finishes within the poll budget', () => {",
                  "    pm.expect.fail('still ' + job.status + ' after ' + maxAttempts + ' polls; raise poll_max_attempts');",
                  "  });",
                  "  goTo(null);",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/api/v1/pipeline/jobs/{{job_id}}",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "pipeline",
                "jobs",
                "{{job_id}}"
              ]
            },
            "description": "Polls until status is completed or failed (queued → in_progress → completed/failed). The document is result.result.text.content (HTML) and document.content; record_id is stored for the corrections."
          },
          "response": [
            {
              "name": "200 completed",
              "originalRequest": {
                "method": "GET",
                "header": [],
                "url": {
                  "raw": "{{base_url}}/api/v1/pipeline/jobs/49908b9e-0ad1-4c0c-8919-3fa5fb94552d",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "api",
                    "v1",
                    "pipeline",
                    "jobs",
                    "49908b9e-0ad1-4c0c-8919-3fa5fb94552d"
                  ]
                }
              },
              "status": "OK",
              "code": 200,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "body": "{\n  \"jobId\": \"49908b9e-0ad1-4c0c-8919-3fa5fb94552d\",\n  \"requestId\": \"...\",\n  \"type\": \"medical_record_generation\",\n  \"external_reference_id\": \"bed-12-night-2026-10-21\",\n  \"state\": \"completed\",\n  \"status\": \"completed\",\n  \"progress\": {\n    \"...\": \"...\"\n  },\n  \"result\": {\n    \"requestId\": \"...\",\n    \"result\": {\n      \"record_id\": \"b3f0c2de-...\",\n      \"text\": {\n        \"title\": \"ICU daily progress note\",\n        \"content\": \"<div>...HTML document...</div>\",\n        \"templateId\": \"6f1c...\",\n        \"succeeded\": true\n      },\n      \"transcript\": \"...\",\n      \"clinical_intelligence\": {\n        \"specialty\": \"MEDICINA_INTENSIVA\",\n        \"...\": \"...\"\n      }\n    }\n  },\n  \"error\": null,\n  \"document\": {\n    \"id\": \"b3f0c2de-...\",\n    \"title\": \"ICU daily progress note\",\n    \"content\": \"<div>...HTML document...</div>\"\n  }\n}"
            }
          ]
        },
        {
          "name": "6. Look jobs up by your reference",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const body = pm.response.json();",
                  "pm.test('200 and the generation job is listed', () => {",
                  "  pm.response.to.have.status(200);",
                  "  pm.expect(body.external_reference_id).to.eql(pm.variables.get('external_reference_id'));",
                  "  pm.expect(body.jobs.map((j) => j.jobId)).to.include(pm.variables.get('job_id'));",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/api/v1/pipeline/jobs?external_reference_id={{external_reference_id}}&limit=10",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "pipeline",
                "jobs"
              ],
              "query": [
                {
                  "key": "external_reference_id",
                  "value": "{{external_reference_id}}"
                },
                {
                  "key": "limit",
                  "value": "10"
                }
              ]
            },
            "description": "Your account’s jobs with this external_reference_id, newest first (limit: default 10, max 50). Use it to recover a jobId whose 202 never arrived."
          },
          "response": []
        },
        {
          "name": "7. Generate a document (sync)",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "// A new submission gets a new Idempotency-Key. To RETRY this same submission",
                  "// (timeout, dropped connection, 5xx), resend it WITHOUT re-running this script:",
                  "// the same key returns the original job and never charges a second credit.",
                  "pm.collectionVariables.set('idempotency_key', pm.variables.replaceIn('{{$guid}}'));",
                  "pm.collectionVariables.set('request_id', pm.variables.replaceIn('{{$guid}}'));"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const body = pm.response.json();",
                  "pm.test('200 with the document, or 202 if it ran past the 90 s cap', () => {",
                  "  pm.expect(pm.response.code).to.be.oneOf([200, 202]);",
                  "});",
                  "if (pm.response.code === 200) {",
                  "  pm.test('the document is inline (HTML)', () => {",
                  "    pm.expect(body.data.status).to.eql('completed');",
                  "    pm.expect(body.data.record_id).to.be.a('string').and.not.empty;",
                  "    pm.expect(body.data.document.content).to.be.a('string').and.not.empty;",
                  "    pm.expect(body.data.external_reference_id).to.eql(pm.variables.get('external_reference_id'));",
                  "  });",
                  "  pm.test('clinical_intelligence: false returns no clinical_intelligence', () => {",
                  "    pm.expect(body.data).to.not.have.property('clinical_intelligence');",
                  "  });",
                  "} else if (pm.response.code === 202) {",
                  "  console.log('Still running at the 90 s cap: poll ' + body.data.statusUrl);",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "text"
              },
              {
                "key": "Idempotency-Key",
                "value": "{{idempotency_key}}",
                "type": "text"
              },
              {
                "key": "X-Request-Id",
                "value": "{{request_id}}",
                "type": "text"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"text\": \"Evolução de UTI, plantão noturno, leito 12. Paciente fictício, 64 anos, em ventilação mecânica em modo PCV, FiO2 de 40% e PEEP de 8. Em uso de noradrenalina a 0,1 micrograma por quilo por minuto, pressão arterial média de 70. Afebril nas últimas 24 horas, diurese de 1.200 mL no período. Mantida sedação com propofol. Plano: reduzir noradrenalina conforme tolerância e realizar teste de respiração espontânea pela manhã.\",\n  \"template_id\": \"{{template_id}}\",\n  \"mode\": \"sync\",\n  \"language\": \"pt-BR\",\n  \"external_reference_id\": \"{{external_reference_id}}\",\n  \"clinical_intelligence\": false\n}"
            },
            "url": {
              "raw": "{{base_url}}/api/v1/medical-records/generate",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "medical-records",
                "generate"
              ]
            },
            "description": "The same generation with mode \"sync\": the request waits up to 90 seconds and answers 200 with the document, or the async 202 if the job is still running. Set your HTTP client timeout above 90 seconds. This example also sends clinical_intelligence: false (document only)."
          },
          "response": []
        },
        {
          "name": "8. Correct the document by text (async)",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "// A new submission gets a new Idempotency-Key. To RETRY this same submission",
                  "// (timeout, dropped connection, 5xx), resend it WITHOUT re-running this script:",
                  "// the same key returns the original job and never charges a second credit.",
                  "pm.collectionVariables.set('idempotency_key', pm.variables.replaceIn('{{$guid}}'));",
                  "pm.collectionVariables.set('request_id', pm.variables.replaceIn('{{$guid}}'));"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "",
                  "const goTo = (name) => (pm.execution && typeof pm.execution.setNextRequest === 'function')",
                  "  ? pm.execution.setNextRequest(name)",
                  "  : postman.setNextRequest(name);",
                  "const body = pm.response.json();",
                  "pm.test('202 with a correction jobId', () => {",
                  "  pm.response.to.have.status(202);",
                  "  pm.expect(body.data.jobId).to.be.a('string').and.not.empty;",
                  "  pm.expect(body.data.external_reference_id).to.eql(pm.variables.get('external_reference_id'));",
                  "});",
                  "if (pm.response.code === 202) {",
                  "  pm.collectionVariables.set('correction_job_id', body.data.jobId);",
                  "  pm.collectionVariables.set('poll_attempt', 0);",
                  "} else {",
                  "  console.error('Correction was not accepted: ' + JSON.stringify(body.error));",
                  "  goTo(null);",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "text"
              },
              {
                "key": "Idempotency-Key",
                "value": "{{idempotency_key}}",
                "type": "text"
              },
              {
                "key": "X-Request-Id",
                "value": "{{request_id}}",
                "type": "text"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"instructions\": \"Alterar a dose de noradrenalina para 0,3 micrograma por quilo por minuto.\",\n  \"mode\": \"async\",\n  \"external_reference_id\": \"{{external_reference_id}}\"\n}"
            },
            "url": {
              "raw": "{{base_url}}/api/v1/medical-records/{{record_id}}/correct-text",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "medical-records",
                "{{record_id}}",
                "correct-text"
              ]
            },
            "description": "Corrects the document from step 5 (record_id). When the job completes the correction is already saved. One correction at a time per document: a concurrent one fails with CORRECTION_CONFLICT."
          },
          "response": []
        },
        {
          "name": "9. Poll the correction job",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "",
                  "const goTo = (name) => (pm.execution && typeof pm.execution.setNextRequest === 'function')",
                  "  ? pm.execution.setNextRequest(name)",
                  "  : postman.setNextRequest(name);",
                  "const job = pm.response.json();",
                  "const maxAttempts = Number(pm.variables.get('poll_max_attempts') || 72);",
                  "const intervalMs = Number(pm.variables.get('poll_interval_ms') || 5000);",
                  "const attempt = Number(pm.collectionVariables.get('poll_attempt') || 0) + 1;",
                  "pm.collectionVariables.set('poll_attempt', attempt);",
                  "",
                  "pm.test('200 with the job', () => {",
                  "  pm.response.to.have.status(200);",
                  "  pm.expect(job.jobId).to.eql(pm.variables.get('correction_job_id'));",
                  "});",
                  "pm.test('external_reference_id comes back in every state', () => {",
                  "  pm.expect(job.external_reference_id).to.eql(pm.variables.get('external_reference_id'));",
                  "});",
                  "",
                  "if (job.status === 'completed') {",
                  "  pm.collectionVariables.set('poll_attempt', 0);",
                  "  pm.test('the correction is saved', () => {",
                  "    pm.expect(job.type).to.eql('document_correction');",
                  "    pm.expect(job.result.saved).to.eql(true);",
                  "    pm.expect(job.result.correctedDocument).to.be.a('string').and.not.empty;",
                  "  });",
                  "  pm.collectionVariables.set('corrected_document', job.result.correctedDocument);",
                  "  console.log('Correction saved after ' + attempt + ' poll(s).');",
                  "} else if (job.status === 'failed') {",
                  "  pm.collectionVariables.set('poll_attempt', 0);",
                  "  pm.test('the job completes', () => {",
                  "    pm.expect.fail('job failed: ' + job.errorCode + ': ' + job.error);",
                  "  });",
                  "  goTo(null);",
                  "} else if (attempt < maxAttempts) {",
                  "  // In the Collection Runner / newman this request runs again after the delay.",
                  "  // A single \"Send\" in the Postman app ignores setNextRequest: send it again by hand.",
                  "  console.log('Attempt ' + attempt + '/' + maxAttempts + ': ' + job.status + '; polling again in ' + intervalMs + ' ms');",
                  "  goTo(pm.info.requestName);",
                  "  setTimeout(() => {}, intervalMs);",
                  "} else {",
                  "  pm.collectionVariables.set('poll_attempt', 0);",
                  "  pm.test('the job finishes within the poll budget', () => {",
                  "    pm.expect.fail('still ' + job.status + ' after ' + maxAttempts + ' polls; raise poll_max_attempts');",
                  "  });",
                  "  goTo(null);",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/api/v1/pipeline/jobs/{{correction_job_id}}",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "pipeline",
                "jobs",
                "{{correction_job_id}}"
              ]
            },
            "description": "Same polling as step 5. On completed, result.saved is true and result.correctedDocument is the saved document."
          },
          "response": []
        },
        {
          "name": "10. Read the corrected document",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const body = pm.response.json();",
                  "pm.test('200 and the record holds the saved correction', () => {",
                  "  pm.response.to.have.status(200);",
                  "  pm.expect(body.data.record.id).to.eql(pm.variables.get('record_id'));",
                  "  pm.expect(body.data.record.content).to.eql(pm.variables.get('corrected_document'));",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/api/v1/medical-records/{{record_id}}",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "medical-records",
                "{{record_id}}"
              ]
            },
            "description": "The document as it is now, including saved corrections."
          },
          "response": []
        },
        {
          "name": "11. Optional: correct by voice",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "",
                  "// A new submission gets a new Idempotency-Key. To RETRY this same submission",
                  "// (timeout, dropped connection, 5xx), resend it WITHOUT re-running this script:",
                  "// the same key returns the original job and never charges a second credit.",
                  "pm.collectionVariables.set('idempotency_key', pm.variables.replaceIn('{{$guid}}'));",
                  "pm.collectionVariables.set('request_id', pm.variables.replaceIn('{{$guid}}'));",
                  "if (!pm.variables.get('correction_audio_base64')) {",
                  "  console.log('Skipped: set correction_audio_base64 (base64 of a short recording, for example \"trocar a dose de noradrenalina para 0,2\") to run this step.');",
                  "  if (pm.execution && typeof pm.execution.skipRequest === 'function') {",
                  "    pm.execution.skipRequest();",
                  "  }",
                  "}"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const body = pm.response.json();",
                  "if (!pm.variables.get('correction_audio_base64')) {",
                  "  pm.test('without audio the API answers 400', () => pm.response.to.have.status(400));",
                  "} else {",
                  "  pm.test('200 with the corrected document, or 202 past the 90 s cap', () => {",
                  "    pm.expect(pm.response.code).to.be.oneOf([200, 202]);",
                  "  });",
                  "  if (pm.response.code === 200) {",
                  "    pm.test('the correction is saved', () => pm.expect(body.data.saved).to.eql(true));",
                  "  }",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "text"
              },
              {
                "key": "Idempotency-Key",
                "value": "{{idempotency_key}}",
                "type": "text"
              },
              {
                "key": "X-Request-Id",
                "value": "{{request_id}}",
                "type": "text"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"audio\": \"{{correction_audio_base64}}\",\n  \"mode\": \"sync\",\n  \"language\": \"pt-BR\",\n  \"external_reference_id\": \"{{external_reference_id}}\"\n}"
            },
            "url": {
              "raw": "{{base_url}}/api/v1/medical-records/{{record_id}}/correct",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "medical-records",
                "{{record_id}}",
                "correct"
              ]
            },
            "description": "Voice correction of the same document, in sync mode. Needs a recording: set correction_audio_base64 (WebM/Opus, M4A, WAV, OGG, FLAC or MP3, base64, body up to 10 MB). Skipped when empty. Use synthetic speech or a recording without patient data."
          },
          "response": []
        }
      ]
    },
    {
      "name": "Integration key (B2B): error examples",
      "description": "Requests that need no credentials and show the error shapes of the integration-key flow. Safe to run against any environment: they create nothing.",
      "auth": {
        "type": "noauth"
      },
      "item": [
        {
          "name": "Exchange an unknown key (401 invalid_key)",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const body = pm.response.json();",
                  "pm.test('401 invalid_key in the authentication error shape', () => {",
                  "  pm.response.to.have.status(401);",
                  "  pm.expect(body.success).to.eql(false);",
                  "  pm.expect(body.error.error).to.eql('invalid_key');",
                  "  pm.expect(body.meta.requestId).to.be.a('string');",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "text"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"integration_key\": \"ik_postman_example_not_a_real_key\"\n}"
            },
            "url": {
              "raw": "{{base_url}}/api/v1/auth/exchange-integration-key",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "auth",
                "exchange-integration-key"
              ]
            }
          },
          "response": []
        },
        {
          "name": "Exchange a value that is not an integration key (400 invalid_key)",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const body = pm.response.json();",
                  "pm.test('400 invalid_key (keys start with ik_)', () => {",
                  "  pm.response.to.have.status(400);",
                  "  pm.expect(body.error.error).to.eql('invalid_key');",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "text"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"integration_key\": \"not-an-integration-key\"\n}"
            },
            "url": {
              "raw": "{{base_url}}/api/v1/auth/exchange-integration-key",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "auth",
                "exchange-integration-key"
              ]
            }
          },
          "response": []
        },
        {
          "name": "Refresh with an unknown refresh token (401 invalid_grant)",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const body = pm.response.json();",
                  "pm.test('401 invalid_grant: exchange the key again', () => {",
                  "  pm.response.to.have.status(401);",
                  "  pm.expect(body.error.error).to.eql('invalid_grant');",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "text"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"refresh_token\": \"postman-example-not-a-real-refresh-token\"\n}"
            },
            "url": {
              "raw": "{{base_url}}/api/v1/auth/refresh",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "auth",
                "refresh"
              ]
            }
          },
          "response": []
        },
        {
          "name": "Poll a job without a token (401 UNAUTHORIZED)",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const body = pm.response.json();",
                  "pm.test('401 UNAUTHORIZED in the API error shape', () => {",
                  "  pm.response.to.have.status(401);",
                  "  pm.expect(body.success).to.eql(false);",
                  "  pm.expect(body.error.code).to.eql('UNAUTHORIZED');",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/api/v1/pipeline/jobs/0",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "pipeline",
                "jobs",
                "0"
              ]
            }
          },
          "response": []
        },
        {
          "name": "API health",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('200 healthy', () => {",
                  "  pm.response.to.have.status(200);",
                  "  pm.expect(pm.response.json().status).to.eql('healthy');",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/api/health",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "health"
              ]
            }
          },
          "response": []
        }
      ]
    },
    {
      "name": "🔐 OAuth2 Authentication",
      "description": "OAuth2 authorization code flow for partner apps acting for a TranscriMed user. Company accounts (B2B) use the Integration key (B2B) folder instead.",
      "item": [
        {
          "name": "1️⃣ Generate Authorization URL",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{oauth_base_url}}/",
              "host": [
                "{{oauth_base_url}}"
              ],
              "path": [
                ""
              ]
            },
            "description": "📋 **Helper request to show OAuth2 authorization URL construction**\n\n**For Partners/Developers:**\nThis shows how to construct the authorization URL. Users should be redirected to:\n\n```\n{{oauth_base_url}}/api/oauth/authorize?response_type=code&client_id={{client_id}}&redirect_uri={{redirect_uri}}&scope=read:medical_records&state=random_state_123\n```\n\n**Implementation Example:**\n```javascript\nconst authUrl = `${oauthBaseUrl}/api/oauth/authorize?` +\n  `response_type=code&` +\n  `client_id=${clientId}&` +\n  `redirect_uri=${encodeURIComponent(redirectUri)}&` +\n  `scope=read:medical_records write:medical_records read:jobs worklists:read worklists:write worklists:manage&` +\n  `state=${generateRandomState()}`;\n\n// Redirect user to authorization page\nwindow.location.href = authUrl;\n```\n\n**After authorization:**\n1. User logs in to TranscriMed\n2. User grants permissions\n3. User is redirected to your callback URL with authorization code\n4. Extract the code parameter and use it in step 2"
          },
          "response": []
        },
        {
          "name": "1️⃣ Authorization Request (Browser Only)",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{oauth_base_url}}/api/oauth/authorize?response_type=code&client_id={{client_id}}&redirect_uri={{redirect_uri}}&scope=read:medical_records write:medical_records read:jobs worklists:read worklists:write worklists:manage&state=random_state_123",
              "host": [
                "{{oauth_base_url}}"
              ],
              "path": [
                "api",
                "oauth",
                "authorize"
              ],
              "query": [
                {
                  "key": "response_type",
                  "value": "code",
                  "description": "OAuth2 response type (always 'code' for authorization code flow)"
                },
                {
                  "key": "client_id",
                  "value": "{{client_id}}",
                  "description": "Your OAuth2 client ID"
                },
                {
                  "key": "redirect_uri",
                  "value": "{{redirect_uri}}",
                  "description": "Your registered callback URL"
                },
                {
                  "key": "scope",
                  "value": "read:medical_records write:medical_records read:jobs worklists:read worklists:write worklists:manage",
                  "description": "Requested permissions (space-separated)"
                },
                {
                  "key": "state",
                  "value": "random_state_123",
                  "description": "CSRF protection state parameter"
                }
              ]
            },
            "description": "🚨 **IMPORTANT: This request MUST be executed in a BROWSER, not Postman!**\n\n**Steps:**\n1. Copy the full URL from this request\n2. Open it in your browser\n3. Log in to TranscriMed (if not already logged in)\n4. Grant permissions to your application\n5. You'll be redirected to: `{{redirect_uri}}?code=AUTHORIZATION_CODE&state=random_state_123`\n6. Copy the `code` parameter value\n7. Paste it into the `{{authorization_code}}` variable for Step 2\n\n**Example redirect:**\n```\nhttps://example.com/callback?code=abc123xyz&state=random_state_123\n```"
          },
          "response": []
        },
        {
          "name": "2️⃣ Exchange Code for Tokens",
          "event": [
            {
              "listen": "test",
              "script": {
                "exec": [
                  "// Test script to automatically save tokens from response",
                  "if (pm.response.code === 200) {",
                  "    try {",
                  "        const response = pm.response.json();",
                  "        ",
                  "        // Save access token",
                  "        if (response.access_token) {",
                  "            pm.collectionVariables.set('access_token', response.access_token);",
                  "            console.log('✅ Access token saved to collection variables');",
                  "        }",
                  "        ",
                  "        // Save refresh token",
                  "        if (response.refresh_token) {",
                  "            pm.collectionVariables.set('refresh_token', response.refresh_token);",
                  "            console.log('✅ Refresh token saved to collection variables');",
                  "        }",
                  "        ",
                  "        // Save token type",
                  "        if (response.token_type) {",
                  "            pm.collectionVariables.set('token_type', response.token_type);",
                  "            console.log('✅ Token type saved:', response.token_type);",
                  "        }",
                  "        ",
                  "        // Save expiration time",
                  "        if (response.expires_in) {",
                  "            pm.collectionVariables.set('expires_in', response.expires_in);",
                  "            const expiryTime = new Date(Date.now() + (response.expires_in * 1000));",
                  "            console.log('✅ Token expires in:', response.expires_in, 'seconds');",
                  "            console.log('✅ Token expires at:', expiryTime.toISOString());",
                  "        }",
                  "        ",
                  "        // Save scopes if provided",
                  "        if (response.scope) {",
                  "            console.log('✅ Granted scopes:', response.scope);",
                  "        }",
                  "        ",
                  "        console.log('🎉 OAuth2 token exchange completed successfully!');",
                  "        console.log('🔑 You can now use the API endpoints with the saved access token.');",
                  "        ",
                  "    } catch (error) {",
                  "        console.error('❌ Error processing token response:', error);",
                  "    }",
                  "} else {",
                  "    console.error('❌ Token exchange failed with status:', pm.response.code);",
                  "    console.error('Response:', pm.response.text());",
                  "}"
                ],
                "type": "text/javascript"
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/x-www-form-urlencoded",
                "type": "text"
              }
            ],
            "body": {
              "mode": "urlencoded",
              "urlencoded": [
                {
                  "key": "grant_type",
                  "value": "authorization_code",
                  "description": "OAuth2 grant type",
                  "type": "text"
                },
                {
                  "key": "code",
                  "value": "{{authorization_code}}",
                  "description": "Authorization code from step 1 (paste here)",
                  "type": "text"
                },
                {
                  "key": "redirect_uri",
                  "value": "{{redirect_uri}}",
                  "description": "Must match the redirect URI from step 1",
                  "type": "text"
                },
                {
                  "key": "client_id",
                  "value": "{{client_id}}",
                  "description": "Your OAuth2 client ID",
                  "type": "text"
                },
                {
                  "key": "client_secret",
                  "value": "{{client_secret}}",
                  "description": "Your OAuth2 client secret",
                  "type": "text"
                }
              ]
            },
            "url": {
              "raw": "{{oauth_base_url}}/api/oauth/token",
              "host": [
                "{{oauth_base_url}}"
              ],
              "path": [
                "api",
                "oauth",
                "token"
              ]
            },
            "description": "🔄 **Exchange authorization code for access and refresh tokens**\n\n**Before running this request:**\n1. Make sure you've completed Step 1 in a browser\n2. Copy the authorization code from the redirect URL\n3. Update the `{{authorization_code}}` variable with the code\n\n**Expected Response:**\n```json\n{\n  \"access_token\": \"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...\",\n  \"refresh_token\": \"def502004a8f...\",\n  \"token_type\": \"Bearer\",\n  \"expires_in\": 3600,\n  \"scope\": \"read:medical_records write:medical_records read:jobs worklists:read worklists:write worklists:manage\"\n}\n```\n\n**What happens after success:**\n- Access token is automatically saved to collection variables\n- Refresh token is saved for future token refresh\n- You can now use all API endpoints that require authentication"
          },
          "response": []
        },
        {
          "name": "3️⃣ Refresh Access Token",
          "event": [
            {
              "listen": "test",
              "script": {
                "exec": [
                  "// Test script to automatically save refreshed tokens",
                  "if (pm.response.code === 200) {",
                  "    try {",
                  "        const response = pm.response.json();",
                  "        ",
                  "        // Save new access token",
                  "        if (response.access_token) {",
                  "            pm.collectionVariables.set('access_token', response.access_token);",
                  "            console.log('✅ New access token saved to collection variables');",
                  "        }",
                  "        ",
                  "        // Save new refresh token (if provided)",
                  "        if (response.refresh_token) {",
                  "            pm.collectionVariables.set('refresh_token', response.refresh_token);",
                  "            console.log('✅ New refresh token saved to collection variables');",
                  "        }",
                  "        ",
                  "        // Update expiration time",
                  "        if (response.expires_in) {",
                  "            pm.collectionVariables.set('expires_in', response.expires_in);",
                  "            const expiryTime = new Date(Date.now() + (response.expires_in * 1000));",
                  "            console.log('✅ New token expires in:', response.expires_in, 'seconds');",
                  "            console.log('✅ New token expires at:', expiryTime.toISOString());",
                  "        }",
                  "        ",
                  "        console.log('🔄 Token refresh completed successfully!');",
                  "        ",
                  "    } catch (error) {",
                  "        console.error('❌ Error processing refresh response:', error);",
                  "    }",
                  "} else {",
                  "    console.error('❌ Token refresh failed with status:', pm.response.code);",
                  "    console.error('Response:', pm.response.text());",
                  "}"
                ],
                "type": "text/javascript"
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/x-www-form-urlencoded",
                "type": "text"
              }
            ],
            "body": {
              "mode": "urlencoded",
              "urlencoded": [
                {
                  "key": "grant_type",
                  "value": "refresh_token",
                  "description": "OAuth2 grant type for refresh",
                  "type": "text"
                },
                {
                  "key": "refresh_token",
                  "value": "{{refresh_token}}",
                  "description": "Refresh token from previous token exchange",
                  "type": "text"
                },
                {
                  "key": "client_id",
                  "value": "{{client_id}}",
                  "description": "Your OAuth2 client ID",
                  "type": "text"
                },
                {
                  "key": "client_secret",
                  "value": "{{client_secret}}",
                  "description": "Your OAuth2 client secret",
                  "type": "text"
                }
              ]
            },
            "url": {
              "raw": "{{oauth_base_url}}/api/oauth/token",
              "host": [
                "{{oauth_base_url}}"
              ],
              "path": [
                "api",
                "oauth",
                "token"
              ]
            },
            "description": "🔄 **Refresh expired access token using refresh token**\n\n**When to use:**\n- When your access token has expired (typically after 1 hour)\n- To get a new access token without requiring user re-authorization\n\n**Prerequisites:**\n- Must have a valid refresh token from a previous token exchange\n- Refresh token must not be expired or revoked\n\n**What this does:**\n- Exchanges your refresh token for a new access token\n- May also provide a new refresh token\n- Automatically saves new tokens to collection variables"
          },
          "response": []
        },
        {
          "name": "4️⃣ Validate Token",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{access_token}}",
                "type": "text"
              }
            ],
            "url": {
              "raw": "{{oauth_base_url}}/api/oauth/validate",
              "host": [
                "{{oauth_base_url}}"
              ],
              "path": [
                "api",
                "oauth",
                "validate"
              ]
            },
            "description": "✅ **Validate current access token and check permissions**\n\n**What this returns:**\n- Token validity status\n- User information associated with the token\n- Granted scopes/permissions\n- Token expiration information\n\n**Use this to:**\n- Verify your token is still valid\n- Check what permissions you have\n- Debug authentication issues"
          },
          "response": []
        },
        {
          "name": "🔎 Get My Profile",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{access_token}}",
                "type": "text"
              }
            ],
            "url": {
              "raw": "{{base_url}}/api/v1/user-profiles/me",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "user-profiles",
                "me"
              ]
            },
            "description": "🔐 **Get the authenticated user's profile**\n\nReturns minimal profile shape used by the PTT client to select specialty-specific prompts and phrase boosters.\n\n**Required Scope:** `profiles:read`"
          },
          "response": []
        }
      ]
    },
    {
      "name": "🏥 Medical Records API",
      "description": "Complete medical records management API endpoints",
      "item": [
        {
          "name": "📋 List Medical Records",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/api/v1/medical-records?limit=20&offset=0",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "medical-records"
              ],
              "query": [
                {
                  "key": "limit",
                  "value": "20",
                  "description": "Number of records to return (max 100)"
                },
                {
                  "key": "offset",
                  "value": "0",
                  "description": "Number of records to skip for pagination"
                },
                {
                  "key": "search",
                  "value": "",
                  "description": "Search term for patient name or record content",
                  "disabled": true
                },
                {
                  "key": "date_from",
                  "value": "",
                  "description": "Filter records from date (YYYY-MM-DD)",
                  "disabled": true
                },
                {
                  "key": "date_to",
                  "value": "",
                  "description": "Filter records to date (YYYY-MM-DD)",
                  "disabled": true
                },
                {
                  "key": "template_id",
                  "value": "",
                  "description": "Filter by template ID",
                  "disabled": true
                },
                {
                  "key": "status",
                  "value": "",
                  "description": "Filter by status (draft, final, archived)",
                  "disabled": true
                }
              ]
            },
            "description": "📋 **Get paginated list of medical records**\n\n**Features:**\n- Pagination with limit/offset\n- Search functionality\n- Date range filtering\n- Template-based filtering\n- Status filtering\n\n**Required Scope:** `read:medical_records`"
          },
          "response": []
        },
        {
          "name": "🆕 Generate Medical Record (Sync)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "text"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"text\": \"Paciente fictício com dor torácica em aperto há 3 horas, irradiando para o braço esquerdo. PA 140/90, FC 88. ECG em ritmo sinusal. Plano: enzimas cardíacas seriadas, radiografia de tórax e avaliação da cardiologia.\",\n  \"mode\": \"sync\",\n  \"template_id\": \"{{template_id}}\",\n  \"language\": \"pt-BR\",\n  \"metadata\": {\n    \"source\": \"postman-collection\"\n  }\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": {
              "raw": "{{base_url}}/api/v1/medical-records/generate",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "medical-records",
                "generate"
              ]
            },
            "description": "🆕 **Generate medical record from text input (synchronous)**\n\n**Input Options:**\n- `text`: Raw clinical text to process\n- `template_id`: Template to structure the output\n- `language`: Language for processing (en, pt, es)\n- `patient_info`: Patient demographic data\n- `metadata`: Additional context information\n\n**Processing:**\n- AI-powered text analysis and structuring\n- Medical terminology normalization\n- Template-based formatting\n- Immediate response (up to 30 seconds)\n\n**Required Scope:** `write:medical_records`"
          },
          "response": []
        },
        {
          "name": "🆕 Generate Medical Record (Async)",
          "event": [
            {
              "listen": "test",
              "script": {
                "exec": [
                  "// The 202 carries data.jobId; poll it at GET /api/v1/pipeline/jobs/{jobId}.",
                  "if (pm.response.code === 202) {",
                  "  const response = pm.response.json();",
                  "  pm.collectionVariables.set('last_job_id', response.data.jobId);",
                  "  console.log('Job ID saved: ' + response.data.jobId + '. Poll ' + response.data.statusUrl);",
                  "}"
                ],
                "type": "text/javascript"
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "text"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"audio\": \"data:audio/wav;base64,UklGRnoGAABXQVZFZm10IBAAAAABAAEAQB8AAEAfAAABAAgAZGF0YQoGAACBhYqFbF1fdJivrJBhNjVgodDbq2EcBj+a2/LDciUFLIHO8tiJNwgZaLvt559NEAxQp+PwtmMcBjiR1/LMeSwFJHfH8N2QQAoUXrTp66hVFApGn+DyvmwhBzaJ1PXUE\",\n  \"mode\": \"async\",\n  \"template_id\": \"{{template_id}}\",\n  \"language\": \"pt-BR\",\n  \"metadata\": {\n    \"source\": \"postman-collection\"\n  }\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": {
              "raw": "{{base_url}}/api/v1/medical-records/generate",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "medical-records",
                "generate"
              ]
            },
            "description": "🆕 **Generate medical record from audio input (asynchronous)**\n\n**Input Options:**\n- `audio`: Base64-encoded audio data\n- `template_id`: Template for structuring output\n- `language`: Language for transcription and processing\n- `audio_settings`: Technical audio parameters\n- `processing_options`: AI processing preferences\n\n**Processing Flow:**\n1. Audio transcription (speech-to-text)\n2. Medical terminology enhancement\n3. Text analysis and structuring\n4. Template-based formatting\n\n**Response:**\n- Returns job ID immediately (HTTP 202)\n- Use Jobs API to track progress\n- Retrieve result when job completes\n\n**Required Scope:** `write:medical_records`"
          },
          "response": []
        },
        {
          "name": "📄 Get Medical Record",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/api/v1/medical-records/:recordId",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "medical-records",
                ":recordId"
              ],
              "variable": [
                {
                  "key": "recordId",
                  "value": "mr_example_123",
                  "description": "Medical record ID"
                }
              ]
            },
            "description": "📄 **Get specific medical record by ID**\n\n**Returns:**\n- Complete medical record data\n- Patient information\n- Clinical content (HTML and Markdown)\n- Metadata and audit trail\n- Version information\n\n**Path Parameter:**\n- `recordId`: Unique medical record identifier\n\n**Required Scope:** `read:medical_records`"
          },
          "response": []
        },
        {
          "name": "✏️ Update Medical Record",
          "request": {
            "method": "PUT",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "text"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"title\": \"Updated Emergency Consultation Note\",\n  \"content\": \"<html><body><h1>Emergency Department Consultation</h1><h2>Chief Complaint</h2><p>45-year-old male presents with acute chest pain and shortness of breath.</p><h2>History of Present Illness</h2><p>Patient reports sudden onset of substernal chest pain approximately 3 hours prior to arrival. Pain is described as pressure-like, 8/10 intensity, radiating to left arm. Associated with mild shortness of breath. Denies nausea, vomiting, or diaphoresis.</p><h2>Physical Examination</h2><ul><li>Vital Signs: BP 140/90, HR 88, RR 16, O2 sat 98% on room air</li><li>Cardiovascular: Regular rate and rhythm, no murmurs, rubs, or gallops</li><li>Pulmonary: Clear to auscultation bilaterally</li><li>Extremities: No edema or cyanosis</li></ul><h2>Assessment and Plan</h2><ol><li>Acute chest pain - rule out ACS<ul><li>Serial cardiac enzymes q6h x 3</li><li>12-lead EKG now and in 6 hours</li><li>Chest X-ray</li><li>Cardiology consultation</li></ul></li><li>Continue monitoring in ED</li><li>Patient education provided regarding symptoms to report</li></ol></body></html>\",\n  \"content_markdown\": \"# Emergency Department Consultation\\n\\n## Chief Complaint\\n45-year-old male presents with acute chest pain and shortness of breath.\\n\\n## History of Present Illness\\nPatient reports sudden onset of substernal chest pain approximately 3 hours prior to arrival. Pain is described as pressure-like, 8/10 intensity, radiating to left arm. Associated with mild shortness of breath. Denies nausea, vomiting, or diaphoresis.\\n\\n## Physical Examination\\n- **Vital Signs:** BP 140/90, HR 88, RR 16, O2 sat 98% on room air\\n- **Cardiovascular:** Regular rate and rhythm, no murmurs, rubs, or gallops\\n- **Pulmonary:** Clear to auscultation bilaterally\\n- **Extremities:** No edema or cyanosis\\n\\n## Assessment and Plan\\n1. **Acute chest pain - rule out ACS**\\n   - Serial cardiac enzymes q6h x 3\\n   - 12-lead EKG now and in 6 hours\\n   - Chest X-ray\\n   - Cardiology consultation\\n2. Continue monitoring in ED\\n3. Patient education provided regarding symptoms to report\",\n  \"patient_info\": {\n    \"name\": \"John Doe\",\n    \"age\": 45,\n    \"gender\": \"M\",\n    \"mrn\": \"MRN-123456\",\n    \"dob\": \"1979-03-15\"\n  },\n  \"metadata\": {\n    \"provider\": \"Dr. Smith\",\n    \"department\": \"Emergency Medicine\",\n    \"location\": \"ED Room 3\",\n    \"updated_by\": \"Dr. Smith\",\n    \"update_reason\": \"Added detailed assessment and plan\"\n  }\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": {
              "raw": "{{base_url}}/api/v1/medical-records/:recordId",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "medical-records",
                ":recordId"
              ],
              "variable": [
                {
                  "key": "recordId",
                  "value": "mr_example_123",
                  "description": "Medical record ID to update"
                }
              ]
            },
            "description": "✏️ **Update existing medical record**\n\n**Updateable Fields:**\n- `title`: Record title/subject\n- `content`: HTML formatted content\n- `content_markdown`: Markdown formatted content\n- `patient_info`: Patient demographic updates\n- `metadata`: Provider and context information\n\n**Features:**\n- Version control (creates new version)\n- Audit trail tracking\n- Content validation\n- Automatic timestamps\n\n**Required Scope:** `write:medical_records`"
          },
          "response": []
        },
        {
          "name": "🗑️ Delete Medical Record",
          "request": {
            "method": "DELETE",
            "header": [],
            "url": {
              "raw": "{{base_url}}/api/v1/medical-records/:recordId",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "medical-records",
                ":recordId"
              ],
              "variable": [
                {
                  "key": "recordId",
                  "value": "mr_example_123",
                  "description": "Medical record ID to delete"
                }
              ]
            },
            "description": "🗑️ **Delete medical record**\n\n**⚠️ Warning:** This action may be irreversible depending on your retention policy.\n\n**Behavior:**\n- Soft delete (marked as deleted, data retained)\n- Hard delete (permanent removal) - admin only\n- Audit trail maintained\n\n**Required Scope:** `write:medical_records`"
          },
          "response": []
        },
        {
          "name": "📚 Get Record Versions",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/api/v1/medical-records/:recordId/versions",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "medical-records",
                ":recordId",
                "versions"
              ],
              "variable": [
                {
                  "key": "recordId",
                  "value": "mr_example_123",
                  "description": "Medical record ID"
                }
              ]
            },
            "description": "📚 **Get all versions of a medical record**\n\n**Returns:**\n- List of all record versions\n- Version metadata (timestamps, authors, changes)\n- Content diffs between versions\n- Audit trail information\n\n**Use Cases:**\n- Review editing history\n- Compliance and audit requirements\n- Restore previous versions\n- Track collaborative changes\n\n**Required Scope:** `read:medical_records`"
          },
          "response": []
        },
        {
          "name": "🎤 Apply Voice Corrections",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "text"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"mode\": \"sync\",\n  \"corrections\": [\n    {\n      \"original\": \"patient has chest pain\",\n      \"corrected\": \"patient has severe substernal chest pain\",\n      \"timestamp\": \"00:01:23\",\n      \"confidence\": 0.95\n    },\n    {\n      \"original\": \"blood pressure is normal\",\n      \"corrected\": \"blood pressure is slightly elevated at 140/90\",\n      \"timestamp\": \"00:02:15\",\n      \"confidence\": 0.89\n    },\n    {\n      \"original\": \"heart sounds are good\",\n      \"corrected\": \"heart sounds are regular with no murmurs\",\n      \"timestamp\": \"00:03:45\",\n      \"confidence\": 0.92\n    }\n  ],\n  \"processing_options\": {\n    \"language\": \"en\",\n    \"preserve_formatting\": true,\n    \"apply_medical_terminology\": true,\n    \"validate_corrections\": true\n  },\n  \"metadata\": {\n    \"corrected_by\": \"Dr. Smith\",\n    \"correction_method\": \"voice_input\",\n    \"session_id\": \"correction_session_456\"\n  }\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": {
              "raw": "{{base_url}}/api/v1/medical-records/:recordId/correct",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "medical-records",
                ":recordId",
                "correct"
              ],
              "variable": [
                {
                  "key": "recordId",
                  "value": "mr_example_123",
                  "description": "Medical record ID to apply corrections to"
                }
              ]
            },
            "description": "🎤 **Apply voice-based corrections to medical record**\n\n**Correction Process:**\n1. Voice input captured and transcribed\n2. Original text segments identified\n3. Corrections applied with AI assistance\n4. Medical terminology validation\n5. Updated record generated\n\n**Input Format:**\n- `corrections`: Array of text replacements\n- `timestamp`: Audio timeline reference\n- `confidence`: AI confidence score\n- `processing_options`: Enhancement settings\n\n**Features:**\n- Intelligent text matching\n- Medical terminology enhancement\n- Batch correction processing\n- Audit trail of changes\n\n**Required Scope:** `write:medical_records`"
          },
          "response": []
        }
      ]
    },
    {
      "name": "⚙️ Jobs API (legacy)",
      "description": "Deprecated for document generation and corrections. These requests target the older `/api/v1/jobs` endpoints, which return the raw job record (no `document`, no `errorCode`, and an `external_reference_id` that is TranscriMed’s request id). The `202` of generation and corrections points to `/api/v1/pipeline/jobs/{jobId}`: use the Integration key (B2B) folder’s polling request instead.",
      "item": [
        {
          "name": "📋 List Jobs",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/api/v1/jobs?limit=20&offset=0",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "jobs"
              ],
              "query": [
                {
                  "key": "limit",
                  "value": "20",
                  "description": "Number of jobs to return (max 100)"
                },
                {
                  "key": "offset",
                  "value": "0",
                  "description": "Number of jobs to skip for pagination"
                },
                {
                  "key": "status",
                  "value": "",
                  "description": "Filter by status: pending, processing, completed, failed",
                  "disabled": true
                },
                {
                  "key": "type",
                  "value": "",
                  "description": "Filter by job type: medical_record_generation, audio_transcription",
                  "disabled": true
                },
                {
                  "key": "created_after",
                  "value": "",
                  "description": "Filter jobs created after date (ISO 8601)",
                  "disabled": true
                }
              ]
            },
            "description": "📋 **Get paginated list of processing jobs**\n\n**Job Types:**\n- `medical_record_generation`: Async medical record creation\n- `audio_transcription`: Audio-to-text processing\n- `document_analysis`: Document processing jobs\n- `batch_processing`: Bulk operations\n\n**Job Statuses:**\n- `pending`: Queued for processing\n- `processing`: Currently being processed\n- `completed`: Successfully finished\n- `failed`: Processing failed\n- `cancelled`: Manually cancelled\n\n**Required Scope:** `read:jobs`"
          },
          "response": []
        },
        {
          "name": "📊 Get Job Status",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/api/v1/jobs/:jobId",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "jobs",
                ":jobId"
              ],
              "variable": [
                {
                  "key": "jobId",
                  "value": "{{last_job_id}}",
                  "description": "Job ID to check status"
                }
              ]
            },
            "description": "📊 **Get detailed job status and progress information**\n\n**Returns:**\n- Current job status and progress percentage\n- Processing timestamps and duration\n- Error information (if failed)\n- Resource usage statistics\n- Estimated completion time\n\n**Polling Recommendations:**\n- Check every 5-10 seconds for active jobs\n- Exponential backoff for long-running jobs\n- Stop polling when status is `completed` or `failed`\n\n**Required Scope:** `read:jobs`"
          },
          "response": []
        },
        {
          "name": "📄 Get Job Result",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/api/v1/jobs/:jobId/result",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "jobs",
                ":jobId",
                "result"
              ],
              "variable": [
                {
                  "key": "jobId",
                  "value": "{{last_job_id}}",
                  "description": "Job ID to get result"
                }
              ]
            },
            "description": "📄 **Get processing result from completed job**\n\n**Availability:**\n- Only available when job status is `completed`\n- Results may expire after retention period\n- Large results may be paginated\n\n**Result Types:**\n- Medical records: Complete record data\n- Transcriptions: Text and metadata\n- Analysis: Structured insights\n- Files: Download URLs for generated content\n\n**Required Scope:** `read:jobs`"
          },
          "response": []
        },
        {
          "name": "📋 Get Job Logs",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/api/v1/jobs/:jobId/logs",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "jobs",
                ":jobId",
                "logs"
              ],
              "variable": [
                {
                  "key": "jobId",
                  "value": "{{last_job_id}}",
                  "description": "Job ID to get logs"
                }
              ]
            },
            "description": "📋 **Get detailed execution logs for job debugging**\n\n**Log Information:**\n- Processing steps and milestones\n- Error messages and stack traces\n- Performance metrics\n- AI model interactions\n- System resource usage\n\n**Use Cases:**\n- Debugging failed jobs\n- Performance optimization\n- Compliance auditing\n- Process improvement\n\n**Required Scope:** `read:jobs`"
          },
          "response": []
        },
        {
          "name": "❌ Cancel/Delete Job",
          "request": {
            "method": "DELETE",
            "header": [],
            "url": {
              "raw": "{{base_url}}/api/v1/jobs/:jobId",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "jobs",
                ":jobId"
              ],
              "variable": [
                {
                  "key": "jobId",
                  "value": "{{last_job_id}}",
                  "description": "Job ID to cancel or delete"
                }
              ]
            },
            "description": "❌ **Cancel running job or delete completed job**\n\n**Behavior by Status:**\n- `pending`: Remove from queue\n- `processing`: Attempt graceful cancellation\n- `completed`: Delete job record and results\n- `failed`: Delete job record and logs\n\n**⚠️ Note:** Cancellation of processing jobs may not be immediate.\n\n**Required Scope:** `jobs:write`"
          },
          "response": []
        }
      ]
    },
    {
      "name": "📝 Templates API",
      "description": "Document template management for medical record generation",
      "item": [
        {
          "name": "📋 List Templates",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/api/v1/templates",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "templates"
              ],
              "query": [
                {
                  "key": "specialty",
                  "value": "",
                  "description": "Filter by medical specialty",
                  "disabled": true
                },
                {
                  "key": "type",
                  "value": "",
                  "description": "Filter by template type",
                  "disabled": true
                },
                {
                  "key": "language",
                  "value": "",
                  "description": "Filter by language (en, pt, es)",
                  "disabled": true
                }
              ]
            },
            "description": "📋 **Get list of available document templates**\n\n**Template Categories:**\n- Consultation notes\n- Progress notes\n- Discharge summaries\n- Procedure reports\n- Specialist reports\n\n**Template Features:**\n- Specialty-specific formatting\n- Multi-language support\n- Customizable fields\n- AI-optimized prompts\n\n**Required Scope:** `read:templates`"
          },
          "response": []
        },
        {
          "name": "🆕 Create Template",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "text"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"name\": \"Cardiology Consultation Template\",\n  \"description\": \"Comprehensive template for cardiology consultation reports with specialized sections for cardiac assessment\",\n  \"content\": \"<html><head><title>Cardiology Consultation</title></head><body><h1>Cardiology Consultation Report</h1><h2>Patient Information</h2><p><strong>Name:</strong> {{patient.name}}</p><p><strong>Age:</strong> {{patient.age}}</p><p><strong>Gender:</strong> {{patient.gender}}</p><p><strong>MRN:</strong> {{patient.mrn}}</p><h2>Consultation Date</h2><p>{{consultation_date}}</p><h2>Referring Physician</h2><p>{{referring_physician}}</p><h2>Chief Complaint</h2><p>{{chief_complaint}}</p><h2>History of Present Illness</h2><p>{{history_present_illness}}</p><h2>Past Medical History</h2><p>{{past_medical_history}}</p><h2>Medications</h2><p>{{medications}}</p><h2>Physical Examination</h2><h3>Vital Signs</h3><p>{{vital_signs}}</p><h3>Cardiovascular</h3><p>{{cardiovascular_exam}}</p><h3>Other Systems</h3><p>{{other_systems}}</p><h2>Diagnostic Tests</h2><h3>EKG</h3><p>{{ekg_findings}}</p><h3>Echocardiogram</h3><p>{{echo_findings}}</p><h3>Laboratory</h3><p>{{lab_results}}</p><h2>Assessment</h2><p>{{assessment}}</p><h2>Plan</h2><p>{{plan}}</p><h2>Follow-up</h2><p>{{followup}}</p><hr><p><em>Consultation completed by: {{provider_name}}<br>Date: {{completion_date}}</em></p></body></html>\",\n  \"specialty\": \"cardiology\",\n  \"type\": \"consultation\",\n  \"language\": \"en\",\n  \"fields\": [\n    {\n      \"name\": \"patient.name\",\n      \"type\": \"text\",\n      \"required\": true,\n      \"description\": \"Patient full name\"\n    },\n    {\n      \"name\": \"patient.age\",\n      \"type\": \"number\",\n      \"required\": true,\n      \"description\": \"Patient age in years\"\n    },\n    {\n      \"name\": \"chief_complaint\",\n      \"type\": \"text\",\n      \"required\": true,\n      \"description\": \"Primary reason for consultation\"\n    },\n    {\n      \"name\": \"cardiovascular_exam\",\n      \"type\": \"text\",\n      \"required\": true,\n      \"description\": \"Cardiovascular examination findings\"\n    },\n    {\n      \"name\": \"assessment\",\n      \"type\": \"text\",\n      \"required\": true,\n      \"description\": \"Clinical assessment and diagnosis\"\n    },\n    {\n      \"name\": \"plan\",\n      \"type\": \"text\",\n      \"required\": true,\n      \"description\": \"Treatment plan and recommendations\"\n    }\n  ],\n  \"ai_instructions\": \"Focus on cardiac-specific terminology and ensure proper documentation of cardiovascular risk factors, examination findings, and evidence-based treatment recommendations. Include relevant cardiology guidelines and risk stratification when applicable.\",\n  \"metadata\": {\n    \"created_by\": \"Dr. Cardiology\",\n    \"version\": \"1.0\",\n    \"category\": \"specialist_consultation\"\n  }\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": {
              "raw": "{{base_url}}/api/v1/templates",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "templates"
              ]
            },
            "description": "🆕 **Create new document template**\n\n**Template Components:**\n- `content`: HTML template with placeholders\n- `fields`: Structured field definitions\n- `ai_instructions`: Guidance for AI processing\n- `specialty`: Medical specialty association\n- `metadata`: Template management info\n\n**Placeholder Syntax:**\n- `{{field_name}}`: Simple field replacement\n- `{{patient.name}}`: Nested object access\n- `{{#if condition}}`: Conditional sections\n- `{{#each items}}`: Iterative sections\n\n**Required Scope:** `write:templates`"
          },
          "response": []
        },
        {
          "name": "📄 Get Template",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/api/v1/templates/:templateId",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "templates",
                ":templateId"
              ],
              "variable": [
                {
                  "key": "templateId",
                  "value": "template_example_123",
                  "description": "Template ID"
                }
              ]
            },
            "description": "📄 **Get specific template by ID**\n\n**Returns:**\n- Complete template definition\n- Field specifications and validation rules\n- AI processing instructions\n- Usage statistics and metadata\n- Version history information\n\n**Use Cases:**\n- Template preview before use\n- Integration planning\n- Custom field mapping\n- AI instruction analysis\n\n**Required Scope:** `read:templates`"
          },
          "response": []
        },
        {
          "name": "✏️ Update Template",
          "request": {
            "method": "PUT",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "text"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"name\": \"Enhanced Cardiology Consultation Template\",\n  \"description\": \"Comprehensive template for cardiology consultation reports with enhanced cardiac assessment sections and updated guidelines\",\n  \"content\": \"<html><head><title>Cardiology Consultation</title><style>body{font-family:Arial,sans-serif;margin:20px}</style></head><body><h1>Cardiology Consultation Report</h1><div class='patient-info'><h2>Patient Information</h2><p><strong>Name:</strong> {{patient.name}}</p><p><strong>Age:</strong> {{patient.age}}</p><p><strong>Gender:</strong> {{patient.gender}}</p><p><strong>MRN:</strong> {{patient.mrn}}</p><p><strong>DOB:</strong> {{patient.dob}}</p></div><h2>Consultation Details</h2><p><strong>Date:</strong> {{consultation_date}}</p><p><strong>Referring Physician:</strong> {{referring_physician}}</p><p><strong>Reason for Referral:</strong> {{referral_reason}}</p><h2>Chief Complaint</h2><p>{{chief_complaint}}</p><h2>History of Present Illness</h2><p>{{history_present_illness}}</p><h2>Past Medical History</h2><p>{{past_medical_history}}</p><h2>Cardiovascular Risk Factors</h2><ul><li><strong>Hypertension:</strong> {{risk_factors.hypertension}}</li><li><strong>Diabetes:</strong> {{risk_factors.diabetes}}</li><li><strong>Hyperlipidemia:</strong> {{risk_factors.hyperlipidemia}}</li><li><strong>Smoking History:</strong> {{risk_factors.smoking}}</li><li><strong>Family History:</strong> {{risk_factors.family_history}}</li></ul><h2>Current Medications</h2><p>{{medications}}</p><h2>Physical Examination</h2><h3>Vital Signs</h3><p>{{vital_signs}}</p><h3>Cardiovascular Examination</h3><p>{{cardiovascular_exam}}</p><h3>Additional Systems</h3><p>{{other_systems}}</p><h2>Diagnostic Studies</h2><h3>Electrocardiogram</h3><p>{{ekg_findings}}</p><h3>Echocardiogram</h3><p>{{echo_findings}}</p><h3>Laboratory Results</h3><p>{{lab_results}}</p><h3>Additional Studies</h3><p>{{additional_studies}}</p><h2>Assessment and Diagnosis</h2><p>{{assessment}}</p><h2>Treatment Plan</h2><p>{{plan}}</p><h2>Recommendations</h2><p>{{recommendations}}</p><h2>Follow-up Instructions</h2><p>{{followup}}</p><hr><div class='signature'><p><em>Consultation completed by: {{provider_name}}, MD<br>Cardiology Department<br>Date: {{completion_date}}<br>Electronic signature applied</em></p></div></body></html>\",\n  \"specialty\": \"cardiology\",\n  \"type\": \"consultation\",\n  \"language\": \"en\",\n  \"fields\": [\n    {\n      \"name\": \"patient.name\",\n      \"type\": \"text\",\n      \"required\": true,\n      \"description\": \"Patient full name\"\n    },\n    {\n      \"name\": \"patient.age\",\n      \"type\": \"number\",\n      \"required\": true,\n      \"description\": \"Patient age in years\",\n      \"validation\": {\n        \"min\": 0,\n        \"max\": 150\n      }\n    },\n    {\n      \"name\": \"risk_factors.hypertension\",\n      \"type\": \"boolean\",\n      \"required\": false,\n      \"description\": \"History of hypertension\"\n    },\n    {\n      \"name\": \"cardiovascular_exam\",\n      \"type\": \"text\",\n      \"required\": true,\n      \"description\": \"Detailed cardiovascular examination findings\"\n    },\n    {\n      \"name\": \"assessment\",\n      \"type\": \"text\",\n      \"required\": true,\n      \"description\": \"Clinical assessment with specific cardiac diagnoses\"\n    },\n    {\n      \"name\": \"recommendations\",\n      \"type\": \"text\",\n      \"required\": true,\n      \"description\": \"Evidence-based treatment recommendations\"\n    }\n  ],\n  \"ai_instructions\": \"Enhanced instructions: Focus on cardiac-specific terminology, ensure proper documentation of cardiovascular risk factors using ACC/AHA guidelines, include detailed examination findings with emphasis on murmurs, gallops, and peripheral vascular findings. Provide evidence-based treatment recommendations with references to current cardiology guidelines. Include risk stratification using appropriate scoring systems when applicable (ASCVD, CHADS-VASc, etc.).\",\n  \"metadata\": {\n    \"updated_by\": \"Dr. Cardiology\",\n    \"version\": \"2.0\",\n    \"category\": \"specialist_consultation\",\n    \"last_reviewed\": \"2024-01-15\",\n    \"guidelines_version\": \"ACC/AHA 2023\"\n  }\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": {
              "raw": "{{base_url}}/api/v1/templates/:templateId",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "templates",
                ":templateId"
              ],
              "variable": [
                {
                  "key": "templateId",
                  "value": "template_example_123",
                  "description": "Template ID to update"
                }
              ]
            },
            "description": "✏️ **Update existing template**\n\n**Update Features:**\n- Version control with change tracking\n- Field validation and migration\n- Backward compatibility checks\n- Usage impact analysis\n\n**Best Practices:**\n- Test template changes in development\n- Document significant changes\n- Consider impact on existing records\n- Update AI instructions for better results\n\n**Required Scope:** `write:templates`"
          },
          "response": []
        },
        {
          "name": "🗑️ Delete Template",
          "request": {
            "method": "DELETE",
            "header": [],
            "url": {
              "raw": "{{base_url}}/api/v1/templates/:templateId",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "templates",
                ":templateId"
              ],
              "variable": [
                {
                  "key": "templateId",
                  "value": "template_example_123",
                  "description": "Template ID to delete"
                }
              ]
            },
            "description": "🗑️ **Delete template**\n\n**⚠️ Deletion Policy:**\n- Templates used in existing records cannot be deleted\n- Archive functionality available for unused templates\n- Permanent deletion requires admin privileges\n- Backup recommended before deletion\n\n**Impact Assessment:**\n- Check template usage before deletion\n- Consider archiving instead of deleting\n- Notify users of template dependencies\n\n**Required Scope:** `write:templates`"
          },
          "response": []
        }
      ]
    },
    {
      "name": "📋 Worklists API (Partner Integration)",
      "description": "External partner API for healthcare system integration. Internal operations (reading, linking, delivery) are handled automatically via the TranscriMed application.",
      "item": [
        {
          "name": "📥 Ingest Worklist Items",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "text"
              },
              {
                "key": "Idempotency-Key",
                "value": "{{$randomUUID}}",
                "description": "Optional idempotency key for duplicate prevention",
                "type": "text",
                "disabled": true
              },
              {
                "key": "X-Source",
                "value": "cs_pacs",
                "description": "Source system identifier (cs_pacs, ris, etc.)",
                "type": "text",
                "disabled": true
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"items\": [\n    {\n      \"accession_number\": \"ACC2024001\",\n      \"patient_id\": \"PT123456\",\n      \"patient_name\": \"John Doe\",\n      \"patient_sex\": \"M\",\n      \"patient_birth_date\": \"1980-03-15\",\n      \"patient_age\": \"44\",\n      \"modality\": \"CT\",\n      \"exam_datetime\": \"2024-08-26T14:30:00Z\",\n      \"exam_room\": \"CT1\",\n      \"exam_description\": \"CT Chest with Contrast\",\n      \"study_uid\": \"1.2.840.113619.2.176.3596.3364818.7819.1234567890.123\",\n      \"procedure_id\": \"CTCHEST001\",\n      \"procedure_step_id\": \"PS001\",\n      \"location\": \"Radiology Department\",\n      \"hospital_name\": \"City General Hospital\",\n      \"referring_physician\": \"Dr. Smith\"\n    },\n    {\n      \"accession_number\": \"ACC2024002\",\n      \"patient_id\": \"PT789012\",\n      \"patient_name\": \"Jane Smith\",\n      \"patient_sex\": \"F\",\n      \"patient_birth_date\": \"1975-07-22\",\n      \"patient_age\": \"49\",\n      \"modality\": \"MRI\",\n      \"exam_datetime\": \"2024-08-26T16:00:00Z\",\n      \"exam_room\": \"MRI2\",\n      \"exam_description\": \"MRI Brain without Contrast\",\n      \"study_uid\": \"1.2.840.113619.2.176.3596.3364818.7819.1234567890.456\",\n      \"procedure_id\": \"MRIBRAIN001\",\n      \"procedure_step_id\": \"PS002\",\n      \"location\": \"Radiology Department\",\n      \"hospital_name\": \"City General Hospital\",\n      \"referring_physician\": \"Dr. Johnson\"\n    }\n  ],\n  \"source\": \"cs_pacs\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": {
              "raw": "{{base_url}}/api/v1/worklists/ingest",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "worklists",
                "ingest"
              ]
            },
            "description": "📥 **Ingest worklist items from partner systems**\n\n**Purpose:**\nBulk import of examination data from PACS, RIS, or other healthcare systems into TranscriMed worklists.\n\n**Input Formats:**\n- Single worklist item as object\n- Array of worklist items\n- Wrapped in `items` array or direct array\n\n**Key Fields:**\n- `accession_number`: Unique exam identifier (required)\n- `patient_id`: Patient identifier (required)\n- `study_uid`: DICOM Study UID (recommended)\n- `modality`: Imaging modality (CT, MRI, X-Ray, etc.)\n- `exam_datetime`: Exam timestamp (ISO 8601)\n\n**Features:**\n- Automatic deduplication by accession number\n- Consistent snake_case field naming\n- Batch processing with detailed results\n- Idempotency key support\n\n**Field Format:**\nAll fields use snake_case naming convention (accession_number, patient_id, exam_datetime, etc.) for API consistency.\n\n**Required Scope:** `worklists:write`"
          },
          "response": []
        },
        {
          "name": "✏️ Update Worklist Item Status",
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "text"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"status\": \"in_progress\",\n  \"reason\": \"Starting transcription for chest CT examination\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": {
              "raw": "{{base_url}}/api/v1/worklists/items/:itemId",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "worklists",
                "items",
                ":itemId"
              ],
              "variable": [
                {
                  "key": "itemId",
                  "value": "12345678-1234-1234-1234-123456789012",
                  "description": "Worklist item UUID"
                }
              ]
            },
            "description": "✏️ **Update worklist item status (Partner/External Use)**\n\n**Purpose:**\nAllows partner systems to update the status of worklist items they have submitted. This is primarily for external system coordination (e.g., cancelling an exam, marking as unavailable).\n\n**Partner Use Cases:**\n- Cancel exam due to patient no-show\n- Mark exam as completed in source system\n- Update status due to equipment issues\n- Reschedule or postpone examinations\n\n**Status Values:**\n- `new`: Item just ingested, awaiting assignment\n- `in_progress`: Item being actively worked on\n- `completed`: Work finished, report generated\n- `cancelled`: Item cancelled by partner/user\n- `archived`: Item moved to archive\n\n**Note:**\nInternal status management (linking to medical records, triggering deliveries) is handled automatically by the TranscriMed application. Partners should only update status for coordination purposes.\n\n**Required Scope:** `worklists:manage`"
          },
          "response": []
        }
      ]
    },
    {
      "name": "🔧 System & Testing",
      "description": "System health checks and API testing utilities",
      "item": [
        {
          "name": "🏥 API Health Check",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/api/health",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "health"
              ]
            },
            "description": "🏥 **Check API server health and status**\n\n**Health Information:**\n- Server status and uptime\n- Database connectivity\n- External service status\n- Performance metrics\n- Version information\n\n**Use Cases:**\n- Service monitoring\n- Deployment verification\n- Troubleshooting connectivity\n- Load balancer health checks\n\n**No authentication required**"
          },
          "response": []
        },
        {
          "name": "🔧 OAuth Server Health",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{oauth_base_url}}/api/health",
              "host": [
                "{{oauth_base_url}}"
              ],
              "path": [
                "api",
                "health"
              ]
            },
            "description": "🔧 **Check OAuth2 server health and status**\n\n**OAuth Health Info:**\n- Authentication service status\n- Token service availability\n- Database connections\n- Rate limiting status\n- Security metrics\n\n**No authentication required**"
          },
          "response": []
        },
        {
          "name": "ℹ️ API Information",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                ""
              ]
            },
            "description": "ℹ️ **Get API information and available endpoints**\n\n**API Information:**\n- API version and build info\n- Available endpoints summary\n- Documentation links\n- Rate limiting information\n- Support contact details\n\n**No authentication required**"
          },
          "response": []
        },
        {
          "name": "🧪 Test Error Handling",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/api/v1/test/error?type=validation",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "test",
                "error"
              ],
              "query": [
                {
                  "key": "type",
                  "value": "validation",
                  "description": "Error type: validation, authorization, server, timeout"
                }
              ]
            },
            "description": "🧪 **Test API error handling and response formats**\n\n**Error Types:**\n- `validation`: Input validation errors\n- `authorization`: Auth/permission errors\n- `server`: Internal server errors\n- `timeout`: Request timeout simulation\n- `rate_limit`: Rate limiting errors\n\n**Use for:**\n- Client error handling testing\n- Error message validation\n- Status code verification\n- Error response format testing\n\n**No authentication required for testing**"
          },
          "response": []
        }
      ]
    }
  ]
}
