openapi: 3.1.0
info:
  title: Auddax API
  version: "2026.08"
  description: |
    The Auddax API runs a governed clinical intake. Create an intake, send
    each patient message as a turn, and read the clinician handoff when the
    intake reaches a terminal state.

    Latency: a turn drives a real model turn in the clinical engine. The
    first streamed token typically arrives within seconds when the engine is
    warm. A complete turn takes 20 to 40 seconds. Use streaming for anything
    patient facing.

    Sandbox terms: this is a research preview. Do not use it for real
    patient care. Do not send personal health information or real patient
    data. Test messages persist in the engine's encounter store.
servers:
  - url: https://api.auddax.ai
security:
  - bearerAuth: []
paths:
  /v1/health:
    get:
      summary: Health and release identity
      security: []
      responses:
        "200":
          description: The gateway is up.
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok: { type: boolean }
                  release: { type: string }
                  build_sha: { type: [string, "null"] }
  /v1/intakes:
    post:
      summary: Create an intake
      description: Starts a new encounter. The optional fields seed clinical context.
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                demographics:
                  type: object
                  properties:
                    age_years: { type: integer, minimum: 0, maximum: 130 }
                    sex: { type: string, enum: [female, male, intersex] }
                patient_history:
                  type: string
                  maxLength: 4000
                  description: Free-text context, for example allergies or current medications.
      responses:
        "201":
          description: The intake is open.
          content:
            application/json:
              schema:
                type: object
                required: [encounter_id, status, created_at, expires_at]
                properties:
                  encounter_id: { type: string }
                  status: { type: string, enum: [open] }
                  created_at: { type: string, format: date-time }
                  expires_at: { type: string, format: date-time }
        "400": { $ref: "#/components/responses/Error" }
        "401": { $ref: "#/components/responses/Error" }
        "429": { $ref: "#/components/responses/Error" }
  /v1/intakes/{encounter_id}/turns:
    post:
      summary: Send one patient message
      description: |
        Drives one intake turn. With `"stream": true` the response is a
        Server-Sent Events stream with these events:

        - `message.delta` `{text}`: the next fragment of the patient-facing reply
        - `message.completed` `{elapsed_ms}`: the reply text is complete
        - `handoff.compiling` `{terminal}`: the engine is compiling the snapshot
        - `handoff.progress` `{chars}`: compile progress
        - `turn.completed`: the same object the blocking response returns
        - `error` `{code, message}`: the turn failed after the stream started

        You may send the next turn as soon as `message.completed` arrives,
        even while the previous turn's handoff is still compiling.

        The `Idempotency-Key` header makes a blocking turn safe to retry:
        a repeated key returns the stored response and does not drive the
        engine again.
      parameters:
        - { name: encounter_id, in: path, required: true, schema: { type: string } }
        - name: Idempotency-Key
          in: header
          required: false
          schema: { type: string }
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [message]
              properties:
                message: { type: string, minLength: 1, maxLength: 8000 }
                stream: { type: boolean, default: false }
      responses:
        "200":
          description: The turn result. Content is SSE when stream is true.
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Turn" }
            text/event-stream:
              schema: { type: string }
        "400": { $ref: "#/components/responses/Error" }
        "401": { $ref: "#/components/responses/Error" }
        "404": { $ref: "#/components/responses/Error" }
        "409": { $ref: "#/components/responses/Error" }
        "410": { $ref: "#/components/responses/Error" }
        "429": { $ref: "#/components/responses/Error" }
        "502": { $ref: "#/components/responses/Error" }
        "503": { $ref: "#/components/responses/Error" }
  /v1/intakes/{encounter_id}:
    get:
      summary: Intake status summary
      parameters:
        - { name: encounter_id, in: path, required: true, schema: { type: string } }
      responses:
        "200":
          description: The stored status of the intake.
          content:
            application/json:
              schema:
                type: object
                properties:
                  encounter_id: { type: string }
                  status: { type: string, enum: [open, closed, expired] }
                  turn_count: { type: integer }
                  created_at: { type: string, format: date-time }
                  expires_at: { type: string, format: date-time }
                  last_safety_status: { type: [string, "null"] }
                  last_disposition: { type: [string, "null"] }
        "401": { $ref: "#/components/responses/Error" }
        "404": { $ref: "#/components/responses/Error" }
  /v1/intakes/{encounter_id}/handoff:
    get:
      summary: Clinician handoff
      description: Readable for open, closed, and expired intakes.
      parameters:
        - { name: encounter_id, in: path, required: true, schema: { type: string } }
      responses:
        "200":
          description: The full handoff snapshot.
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Handoff" }
        "401": { $ref: "#/components/responses/Error" }
        "404": { $ref: "#/components/responses/Error" }
        "429": { $ref: "#/components/responses/Error" }
        "502": { $ref: "#/components/responses/Error" }
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: An Auddax API key, prefix adx_sb_.
  responses:
    Error:
      description: The error envelope.
      content:
        application/json:
          schema:
            type: object
            required: [error]
            properties:
              error:
                type: object
                required: [code, message]
                properties:
                  code:
                    type: string
                    enum:
                      - invalid_request
                      - unauthorized
                      - not_found
                      - intake_closed
                      - intake_expired
                      - rate_limited
                      - quota_exceeded
                      - upstream_error
                      - unavailable
                      - internal_error
                  message: { type: string }
                  encounter_id: { type: string }
  schemas:
    Turn:
      type: object
      required: [encounter_id, turn_index, assistant_message, terminal]
      properties:
        encounter_id: { type: string }
        turn_index: { type: integer }
        assistant_message:
          type: string
          description: The engine's next patient-facing message. Relay it without edits.
        choices:
          type: array
          items: { type: string }
          description: Engine-suggested quick replies for the next turn.
        terminal:
          type: boolean
          description: True when the intake reached a terminal state. No more turns are accepted.
        urgent:
          type: boolean
          description: True when the engine requires immediate clinician review.
        safety_status: { type: [string, "null"] }
        disposition: { type: [string, "null"] }
        disposition_label: { type: [string, "null"] }
        handoff:
          $ref: "#/components/schemas/Handoff"
          description: Present when terminal is true.
    Handoff:
      type: object
      properties:
        encounter_id: { type: string }
        captured_at: { type: string, format: date-time }
        terminal: { type: boolean }
        urgent: { type: boolean }
        intake_status: { type: [string, "null"] }
        safety_status: { type: [string, "null"] }
        safety_notes:
          type: array
          items: { type: string }
        disposition: { type: [string, "null"] }
        disposition_label: { type: [string, "null"] }
        soap:
          type: object
          properties:
            subjective: { type: string }
            objective: { type: string }
            assessment: { type: string }
            plan: { type: string }
        captured_facts:
          type: object
          properties:
            age: { type: [string, "null"] }
            sex: { type: [string, "null"] }
            pregnancy: { type: [string, "null"] }
        active_protocol: { type: [string, "null"] }
        candidate_protocols:
          type: array
          items: { type: string }
        scores:
          type: array
          items:
            type: object
            properties:
              key: { type: string }
              total: { type: string }
              band: { type: string, enum: [strong, mid, weak] }
              components: { type: string }
        conversation:
          type: array
          items:
            type: object
            properties:
              role: { type: string, enum: [assistant, patient] }
              text: { type: string }
        provenance:
          type: object
          description: Server-attested run identity for audit and version pinning.
          properties:
            release: { type: [string, "null"] }
            server_model: { type: [string, "null"] }
            prompt_hash: { type: [string, "null"] }
            source_commit: { type: [string, "null"] }
            directory_hash: { type: [string, "null"] }
            runtime_profile: { type: [string, "null"] }
            prompt_profile: { type: [string, "null"] }
            voice_profile: { type: [string, "null"] }
            audit_ref: { type: [string, "null"] }
            latency_ms: { type: [number, "null"] }
            protocol_count: { type: [number, "null"] }
