# Auddax API > Auddax runs the MedCanon clinical intake engine behind a REST API. You send > patient messages. The engine asks one follow-up question per turn, screens > for red flags, and closes the intake with a clinician handoff, an urgent > escalation, or a not-eligible close. This is a sandbox: synthetic patients > only, never real patient data, never real care. - Base URL: https://api.auddax.ai - Auth: `Authorization: Bearer ` on every /v1 call except /v1/health, /v1/openapi.json, and /v1/openapi.yaml. This page needs no key. - Full spec (JSON): https://api.auddax.ai/v1/openapi.json - Human docs: https://www.auddax.ai/docs - Testing with an agent: https://www.auddax.ai/docs#agents ## Engine behavior The default engine behavior mirrors Certuma Link US production: - One question per turn. Some turns are a red-flag screen with several choices: `choice_mode: "multiple"`. Answer every choice that applies in one message, or say none apply. - Patients under 18 are not eligible: the intake closes with `outcome: "not_eligible"` and no clinician handoff. - A declined self-harm question is accepted once and not asked again. - An emergency closes the intake at once with advice to call emergency services: `outcome: "urgent"`. - The engine closes any other interview only on a done signal: the patient says it has nothing more to add. This lane has no turn cap. - The patient-facing text uses US resources (911, 988). `config.locale` `es-AR` and `pt-BR` change the language, not those resources. `GET /v1/health` names the engine lane and its build. Every handoff has `provenance.source_commit`. Quote both when you report a finding. ## The evaluation loop 1. Create an intake. Tag it so you can find it later. `POST /v1/intakes` `{"label": "sore-throat-01", "metadata": {"arm": "default", "run": "3"}, "demographics": {"age_years": 34, "sex": "female"}, "patient_history": "Prior visit, 2026-08-20, signed clinician record: asthma."}` All fields are optional. Unknown fields are refused (400). 2. Send one patient message per turn. You play the patient. Answer only from your scenario, and say "I don't know" to anything it does not cover. When the scenario has nothing more to give, you must say so: "That's all. I have nothing more to add." `POST /v1/intakes/{encounter_id}/turns` with header `Idempotency-Key: ` and body `{"message": "I have had a sore throat for two days."}` Read `assistant_message`, `choices`, `choice_mode`, and `outcome`. 3. Repeat step 2 while `outcome` is `"continue"`. Stop on `"complete"`, `"urgent"`, or `"not_eligible"` (then `terminal` is true). 4. Read the result. `GET /v1/intakes/{encounter_id}/result` returns one flat object to assert on: outcome, disposition, safety_status, active_protocol, red_flags, clinical_assessment, cannot_miss_screen_incomplete, turn_count, and provenance. `GET /v1/intakes/{encounter_id}/handoff` returns the full clinician handoff and the transcript. 5. Collect runs. `GET /v1/intakes?label=sore-throat-01` lists your intakes, newest first, with outcome and turn count. It is free. ## Close the interview Without a done signal the intake stays open, and you spend turns until your own limit, the daily quota, or the 120-minute session limit stops you. - "I don't know" is not a done signal. Repeated non-answers are not a done signal. Repeating the complaint is not a done signal. - After the first done signal, the engine can ask one more question, only when a cannot-miss safety item is still missing. Answer it, then say the done signal again. The engine then closes. - Set your own turn limit as well, so a run that never closes still ends. ## Which field to read - Triage: `disposition`. It is `clinician_review`, `self_care`, `urgent_escalation`, or `not_eligible`, and `continue_intake` while the intake is open. `disposition_label` is its display text. `urgent` is true when the engine requires immediate clinician review. - Diagnosis: `clinical_assessment.working_diagnosis`, with `differentials` and `management`. It is on the result, the handoff, and the handoff in a terminal turn. It can read "Undetermined" with the reason. `clinical_assessment` is null when the engine did not write one: an open intake, a not-eligible close, or a failed note. - Not triage: `outcome` says how the intake ended. `safety_status` is the state of the cannot-miss screen. `safety_triage` is only urgent or routine. - Not the diagnosis: `active_protocol` and `detected_protocol` name a protocol. `soap.assessment` is prose for the note. ## Language - `config.locale` takes `en-US` (the default), `es-AR`, or `pt-BR`. Any other value is refused (400), for example `es-MX` or `es`. - Set it in `POST /v1/intakes`. It applies to every turn of the intake. For a patient who writes in Spanish use `es-AR`. For Portuguese use `pt-BR`. - The engine writes to the patient in the locale, not in the language the patient writes in. With the default, a patient who writes in Spanish gets replies in English. - On `auddax-sandbox` the clinician fields come back in English in every locale: `soap`, `clinician_summary`, and `clinical_assessment`. - In `pt-BR`, the not-eligible close and the self-harm support line are fixed text in English. ## HTTP timeouts - A turn takes 10 to 40 seconds. A closing turn can take longer, because the engine writes the note. Set your HTTP timeout to at least 300 seconds. Many agent tool runners default to 30 or 60 seconds, which cuts turns off. - `api.auddax.ai` runs through a proxy. When a response has not started in 120 seconds, the proxy returns its own 502. That body can be something other than JSON, so read the status before you parse it. The turn can still finish. Treat it as a client timeout. - Streaming (`stream: true`) sends the first text in a few seconds. ## Retries: never send a patient message twice `Idempotency-Key` protects blocking turns only. A stream has no key: if a stream drops, read `GET /v1/intakes/{encounter_id}` before you send anything. - After a client timeout, a network error, or a 502 from the proxy, retry the SAME request with the SAME Idempotency-Key. You get the stored result, or `409 turn_in_progress` while the first request still runs. Wait `Retry-After` seconds and retry. - Or read `GET /v1/intakes/{encounter_id}`: `turn_in_progress` and `last_turn` (the last reply) tell you whether the message landed. - `409 turn_in_progress` without a key: another turn on this intake is still producing its reply. Nothing was sent. One turn at a time per intake. - `504 upstream_timeout`: the engine was slow. The turn may still finish. Read `GET /v1/intakes/{encounter_id}` before you send anything. Wait until `turn_in_progress` is false. - `409 turn_outcome_unknown`: you retried a key whose request timed out. Do not retry that key. Read `GET /v1/intakes/{encounter_id}/handoff`: if your message is in `conversation`, the engine has it; if not, send it again with a new Idempotency-Key. ## Other errors Every error is `{"error": {"code": "...", "message": "...", "encounter_id"?}}`. - `400 invalid_request`: fix the request. The message names the field. - `400 mode_unavailable`: that mode is not enabled here. Use the default. - `401 unauthorized`: missing or bad key. - `409 intake_closed`, `410 intake_expired`: start a new intake. - `429 rate_limited`: per-minute limit. Wait `Retry-After`. - `429 quota_exceeded`: daily limit. It resets at 00:00 UTC (`Retry-After`). - `502 upstream_error`, `503 unavailable`: engine problem. Retry later with the same Idempotency-Key. ## Limits and free reads - `GET /v1/usage` shows the per-minute limit and the turns left today. Responses carry `x-auddax-turns-remaining`. - Free (no per-minute cost): `GET /v1/intakes`, `GET /v1/intakes/{id}`, `GET /v1/usage`, `GET /v1/usage/history`, `GET /v1/config`, `GET /v1/key`, `GET /v1/health`. ## Two routes that are not the interview `POST /v1/evaluate` with no `mode` is a separate older deterministic path. It is not the consult engine. It reads a transcript with fixed rules: no model, no questions, no `clinical_assessment`, no turns used. Do not use its result to judge the consult engine. `POST /v1/transcripts/assess` is a research read of a finished transcript, in one pass. It is not for clinical use. It forces a diagnosis, which the live product does not force. It is off by default: a key that is not enabled gets `503 unavailable`, and a retry does not change that. Ask your Auddax contact. - Body: `{"messages": [{"role": "patient"|"clinician", "content": "..."}], "locale"?, "label"?}`. At most 1 MiB, 600 messages, and 120000 transcript characters. An `Idempotency-Key` header is required. - You must cut each transcript before the first clinician message that gives a diagnosis or a plan. The route cannot clean a transcript. - Treatment options carry no doses, by design, and the clinician decides. - A chat judged to be about a person under 18 returns `status: "not_eligible"`. - Read `clinical_assessment.working_diagnosis` for the diagnosis and `disposition` for triage. `research_read` is always true. - One call uses one turn of the quota and one request of the per-minute limit. - The gateway waits up to 300 seconds. Set your HTTP timeout to at least 330 seconds. A read can pass the proxy's 120 seconds, so call this route on the direct base URL your Auddax contact gives you. - The gateway stores no transcript text and no output, so it cannot replay a result. A used key gets `409 operation_conflict`. To run the read again, send a new key: a new model pass and a new quota unit. ## Endpoints - `POST /v1/intakes`: create an intake - `GET /v1/intakes`: list your intakes (`label`, `status`, `limit`, `before`) - `GET /v1/intakes/{id}`: status, outcome, `turn_in_progress`, `last_turn` - `POST /v1/intakes/{id}/turns`: send one patient message (`stream: true` for SSE) - `GET /v1/intakes/{id}/result`: compact result for assertions - `GET /v1/intakes/{id}/handoff`: full clinician handoff and transcript - `POST /v1/batches`, `GET /v1/batches/{id}`: scripted scenarios, run server side - `POST /v1/evaluate`: deterministic scoring of a transcript - `POST /v1/transcripts/assess`: research read of a finished transcript (off by default) - `GET /v1/usage`, `GET /v1/config`, `GET /v1/key`, `GET /v1/health` - `GET /v1/openapi.json`, `GET /v1/openapi.yaml`, `GET /llms.txt` With `stream: true`, `message.delta` text can differ from the final reply. The `turn.completed` event's `assistant_message` is the authoritative text.