# TranscriMed API Postman Collection

`transcrimed-api.postman_collection.json` holds the TranscriMed API requests for Postman and [newman](https://github.com/postmanlabs/newman). The environments are in `environments/`.

## Which folder to use

| Folder | For |
|--------|-----|
| **Integration key (B2B)** | Companies that integrate with a company account and an integration key. Runs the whole flow in order: exchange the key, refresh, list templates, generate (async), poll, look up by reference, generate (sync), correct by text, poll, read the corrected document, and optionally correct by voice. |
| **Integration key (B2B): error examples** | Requests that need no credentials and show the error shapes (invalid key, invalid refresh token, missing token) and the health check. |
| OAuth2 Authentication, Medical Records, Templates, Worklists | Partner apps that act for a TranscriMed user through the OAuth2 authorization code flow. They use the collection-level token `{{access_token}}`. |
| Jobs API (legacy) | The older `/api/v1/jobs` endpoints, deprecated for generation and corrections. Poll `/api/v1/pipeline/jobs/{jobId}` instead, as the B2B folder does. |

The B2B folder follows the [Integration Key guide](https://developers.transcrimed.com.br/integration-keys) step by step. Read the guide for the full contract (errors, retries, limits).

## Environments

| File | `base_url` | Use |
|------|------------|-----|
| `Sandbox.postman_environment.json` | `https://api.sandbox.transcrimed.com.br` | Integration with a **sandbox** integration key. Start here. |
| `Production.postman_environment.json` | `https://api.transcrimed.com.br` | Real use, with a production key (or OAuth2 production credentials). |
| `Test.postman_environment.json` | `https://api.transcrimed.com.br` | OAuth2 partner apps only: your client's **test** credentials (`test_tc_...` / `test_tcs_...`) make generation answer mock data without consuming credits. Integration keys have no test mode: use the sandbox. |
| `Local-Development.postman_environment.json` | `http://localhost:3001` | The local API server (`cd server && pnpm dev`) with the local Supabase stack (`pnpm supabase:start`). |

The sandbox is a separate installation: a sandbox key does not work in production, nor the other way round. Use only fictitious data in the sandbox.

Every credential (`integration_key`, `client_secret`) ships **empty**. Set it in Postman's *current value* (which is not synced or exported) or pass it on the newman command line. Never commit a key.

## Run the B2B flow

### In Postman

1. Import the collection and `environments/Sandbox.postman_environment.json`, and select the **Sandbox** environment.
2. Set `integration_key` (it starts with `ik_`) as the current value.
3. Open the **Integration key (B2B)** folder and run it with the **Collection Runner**.

A single **Send** of a polling request (steps 5 and 9) does not loop: send it again until `status` is `completed`.

### With newman

```bash
npx newman run transcrimed-api.postman_collection.json \
  -e environments/Sandbox.postman_environment.json \
  --env-var integration_key="$TRANSCRIMED_INTEGRATION_KEY" \
  --folder "Integration key (B2B)"
```

The error examples need no key:

```bash
npx newman run transcrimed-api.postman_collection.json \
  -e environments/Sandbox.postman_environment.json \
  --folder "Integration key (B2B): error examples"
```

### What the scripts do

- **Tokens.** Step 1 stores `ik_access_token` and `ik_refresh_token`; step 2 replaces both (always keep the most recent refresh token). Every B2B request uses `ik_access_token`, never the OAuth2 `access_token`. If the exchange fails, the run stops there.
- **Template.** Step 3 recomputes `template_id` from the current account's templates on every run (a value from another environment or account is never reused): it takes `template_id_override` when you set it and it belongs to the account, otherwise the account's first template.
- **Idempotency-Key.** Each submission (steps 4, 7, 8, 11) gets a fresh `Idempotency-Key` and `X-Request-Id` from its pre-request script. To *retry* a submission, resend it without re-running that script: the same key returns the original job and never charges a second credit.
- **Reference.** Step 4 sets `external_reference_id` to `postman-demo-<timestamp>`; the generation and the correction carry it, and step 6 looks the jobs up by it.
- **Polling.** Steps 5 and 9 poll every `poll_interval_ms` (default 5000) and re-run themselves up to `poll_max_attempts` times (default 72, about 6 minutes), then stop the run with a failed test. The sandbox sleeps when idle: the first job after a quiet period can stay `queued` for minutes while it wakes up, so keep the default budget or raise it.
- **Sync.** Step 7 waits up to 90 seconds and answers `200` with the document, or the async `202` if the job is still running; both pass. It also sends `clinical_intelligence: false` (document only).
- **Voice correction.** Step 11 needs a recording: set `correction_audio_base64` (base64 of a short WebM/Opus, M4A, WAV, OGG, FLAC or MP3 file, without patient data). It is skipped while the variable is empty.

### Credits and data

Each full run generates **two documents** (steps 4 and 7), and each consumes one credit of the account; corrections consume none. The input is a short, invented ICU progress note in Portuguese. Never send real patient data to the sandbox, and never put patient-identifying data in `external_reference_id` or `metadata`.

## Troubleshooting

| Response | Meaning |
|----------|---------|
| `400 invalid_request` on step 1 | `integration_key` is empty. |
| `401 invalid_key` / `key_revoked` / `key_inactive` / `key_expired` | The key is not valid in this environment. A sandbox key works only with the Sandbox environment. |
| `401 invalid_grant` on step 2 | The refresh token was already used or expired. Run step 1 again. |
| `400 IDEMPOTENCY_KEY_REQUIRED` | The `Idempotency-Key` header is missing: run the submission's pre-request script. |
| `409 IDEMPOTENCY_KEY_REUSED` | The same key was sent with a different body. Let the pre-request script generate a new key. |
| `400 TEMPLATE_NOT_FOUND` | `template_id` is not a template of this account. Rerun step 3 (it recomputes `template_id`); if you set `template_id_override`, use an id from this account. |
| Job `failed` with `TEMPLATE_UNRESOLVED` | No `template_id` and no template could be selected. Create a template in the web app, or choose a standard template on its **Modelos** page. |
| Poll budget exhausted while `queued` | The sandbox was waking up. Raise `poll_max_attempts` and send step 5 again. |
