For the complete documentation index, see llms.txt. This page is also available as Markdown.

Scribe

Inspect Scribe's provider-session and artifact HTTP contracts using operation blocks rendered from its separate public OpenAPI schema.

Scribe has a separate API host and provider-principal access model. Use the base assigned to your deployment and the scope required by the operation. The public schema linked here is the US contract; its availability does not establish enrollment or deployment in another region.

Start with Capture to Finalized Note for the workflow and Scribe authentication for provider access. Sessions and Artifacts explain lifecycle and recovery semantics.

The operation blocks below render the canonical request and response contracts. Health and readiness probes are excluded from this customer reference. The Zoom OAuth callback is a redirect target for the authorization flow, not an operation to call directly.

List Appointments

get

List appointments over a rolling date-anchored window (workspace + provider read-scoped).

Phase 38 ships a deliberate static stub sourced from the superscribe-web seed (see src/appointments.py); the response contract is stable so the source can later swap to a downstream customer API via a managed External Integration without a wire change. TESTING STOPGAP (phase 45a, SUPERSEDED BY PHASE 45): the stub emits a rolling window [today - back, today + forward] (env-tunable, defaults 14/30 → 360 items max) instead of today-only, so date-pinned appointment ids stay in-list across day-rollovers and the phase-41 session join keeps linking a session created on a prior day. Paginated to mirror the sibling list-sessions endpoint (offset-based limit + opaque continuation_token); consumers page the full window and bucket into past/today/upcoming client-side.

Optional date-range filter (phase 34): start_after (INCLUSIVE) and start_before (EXCLUSIVE) are timezone-aware ISO-8601 datetime bounds on the appointment start (normalized to UTC; a naive, date-only or malformed bound is a 422), forming the half-open window [start_after, start_before). Either may be given alone; both omitted preserves the legacy behavior. The filter is applied server-side over the window BEFORE pagination, so a client that needs only its today+tomorrow slice fetches exactly that instead of paging a wide window and filtering client-side. An inverted range or a span wider than _MAX_APPOINTMENT_RANGE returns 400 invalid_date_range.

EHR-backed workspaces (ehr-note-writeback phase 03 — the EHR-integrated workspace with its flag on) fetch exactly the client's window, so BOTH start_after and start_before are REQUIRED there (omitting either is 400 invalid_date_range); the window is never derived server-side.

Path parameters
workspace_idstring · uuidRequired
Query parameters
limitinteger · min: 1 · max: 200OptionalDefault: 50
continuation_tokenanyOptional
start_afterstring · date-time · nullableOptional
start_beforestring · date-time · nullableOptional
Responses
200

Successful Response

application/json
has_morebooleanRequired
continuation_tokenanyOptional
get/v1/{workspace_id}/appointments
GET /v1/{workspace_id}/appointments HTTP/1.1
Accept: */*
{
  "items": [
    {
      "id": "text",
      "session": {
        "id": "123e4567-e89b-12d3-a456-426614174000",
        "status": "in-progress",
        "external_appointment_id": "text",
        "started_at": "2026-01-01T00:00:00.000Z",
        "ended_at": "2026-01-01T00:00:00.000Z",
        "created_at": "2026-01-01T00:00:00.000Z",
        "updated_at": "2026-01-01T00:00:00.000Z"
      },
      "start": "2026-01-01T00:00:00.000Z",
      "end": "2026-01-01T00:00:00.000Z",
      "duration_minutes": 1,
      "reason": "text",
      "appointment_type": "text",
      "patient_name": "text",
      "patient_entity_id": "text",
      "practitioner_name": "text",
      "practitioner_entity_id": "text",
      "location_name": "text",
      "date_of_birth": "2026-01-01",
      "mrn": "text",
      "active_conditions": [
        "text"
      ],
      "medications_to_review": [
        "text"
      ]
    }
  ],
  "has_more": true,
  "continuation_token": null
}

Get Appointment

get

Get one appointment by id. An EHR-backed workspace reads the single appointment straight from the EHR by id (ehr-note-writeback phase 59) — no schedule-window fetch-and-scan, so a busy provider's window can't time out, the root cause of the appointments_unavailable 503. A genuine missing appointment is 404; a failed EHR fetch is 503 appointments_unavailable (never masked as not-found). Every other workspace resolves the id against the static seed.

Path parameters
workspace_idstring · uuidRequired
appointment_idstring · max: 512Required
Responses
200

Successful Response

application/json
idstring · max: 512Required
startstring · date-timeRequired
endstring · date-timeRequired
duration_minutesintegerRequired
reasonstring · max: 256Required
appointment_typestring · max: 256Required
patient_namestring · max: 256Required
patient_entity_idstring · max: 512Required
practitioner_namestring · max: 256Required
practitioner_entity_idstring · max: 256Required
location_namestring · max: 256Required
date_of_birthstring · date · nullableOptional
mrnstring · max: 64 · nullableOptional
active_conditionsstring[] · max: 100Optional
medications_to_reviewstring[] · max: 100Optional
get/v1/{workspace_id}/appointments/{appointment_id}
GET /v1/{workspace_id}/appointments/{appointment_id} HTTP/1.1
Accept: */*
{
  "id": "text",
  "session": {
    "id": "123e4567-e89b-12d3-a456-426614174000",
    "status": "in-progress",
    "external_appointment_id": "text",
    "started_at": "2026-01-01T00:00:00.000Z",
    "ended_at": "2026-01-01T00:00:00.000Z",
    "created_at": "2026-01-01T00:00:00.000Z",
    "updated_at": "2026-01-01T00:00:00.000Z"
  },
  "start": "2026-01-01T00:00:00.000Z",
  "end": "2026-01-01T00:00:00.000Z",
  "duration_minutes": 1,
  "reason": "text",
  "appointment_type": "text",
  "patient_name": "text",
  "patient_entity_id": "text",
  "practitioner_name": "text",
  "practitioner_entity_id": "text",
  "location_name": "text",
  "date_of_birth": "2026-01-01",
  "mrn": "text",
  "active_conditions": [
    "text"
  ],
  "medications_to_review": [
    "text"
  ]
}

Get Provider Settings

get

The calling provider's persisted scribe settings (currently the default Zoom meeting link).

Returns {zoom_meeting_link: null} when the provider has no settings row yet -- an unset link is a valid state, never a 404. Provider-scoped via _provider_id; decoupled from the Zoom OAuth connection, so a saved link persists whether or not Zoom is connected.

Path parameters
workspace_idstring · uuidRequired
Responses
200

Successful Response

application/json

Response of GET/PUT /provider/settings (V366).

The provider's persisted scribe settings. zoom_meeting_link is the server-side default Zoom meeting link, decoupled from the Zoom OAuth connection; null means the provider has never saved one (or has cleared it) -- an unset link is a valid state, never a 404.

zoom_meeting_linkstring · max: 2048 · nullableOptional
get/v1/{workspace_id}/provider/settings
GET /v1/{workspace_id}/provider/settings HTTP/1.1
Accept: */*
{
  "zoom_meeting_link": "text"
}

Update Provider Settings

put

Persist the calling provider's default Zoom meeting link (upsert), or clear it with a null.

The request model validates a non-null link (an http(s) zoom.us URL, personal-room aware, <=2048) via the SAME ZoomMeetingLink type the create-zoom-session request uses, so both writes accept identical links; a null clears the stored link. The normalized str(HttpUrl) form is stored (HttpUrl lowercases the host and adds a trailing slash to a bare origin — a no-op for a real meeting link, which always carries a path). Provider-scoped via _provider_id. Returns the persisted settings.

Path parameters
workspace_idstring · uuidRequired
Body

Body of PUT /provider/settings.

Sets the provider's default Zoom meeting link, or clears it with an explicit null. A non-null link is validated as a zoom.us http(s) URL (personal-room aware, <=2048) via the shared ZoomMeetingLink type the create-zoom-session request also uses. The wire value stays a plain JSON string; the handler stores the normalized str(HttpUrl).

zoom_meeting_linkstring · uri · min: 1 · max: 2083 · nullableOptional
Responses
200

Successful Response

application/json

Response of GET/PUT /provider/settings (V366).

The provider's persisted scribe settings. zoom_meeting_link is the server-side default Zoom meeting link, decoupled from the Zoom OAuth connection; null means the provider has never saved one (or has cleared it) -- an unset link is a valid state, never a 404.

zoom_meeting_linkstring · max: 2048 · nullableOptional
put/v1/{workspace_id}/provider/settings
PUT /v1/{workspace_id}/provider/settings HTTP/1.1
Content-Type: application/json
Accept: */*
Content-Length: 43

{
  "zoom_meeting_link": "https://example.com"
}
{
  "zoom_meeting_link": "text"
}

List Sessions

get
Path parameters
workspace_idstring · uuidRequired
Query parameters
limitinteger · min: 1 · max: 200OptionalDefault: 50
continuation_tokenanyOptional
created_afterstring · date-time · nullableOptional
created_beforestring · date-time · nullableOptional
Responses
200

Successful Response

application/json
has_morebooleanRequired
continuation_tokenanyOptional
get/v1/{workspace_id}/sessions
GET /v1/{workspace_id}/sessions HTTP/1.1
Accept: */*
{
  "items": [
    {
      "id": "123e4567-e89b-12d3-a456-426614174000",
      "status": "in-progress",
      "mode": "in_person",
      "external_appointment_id": "text",
      "first_name": "text",
      "last_name": "text",
      "visit_type": "text",
      "note_template": "amd-psych-intake",
      "started_at": "2026-01-01T00:00:00.000Z",
      "ended_at": "2026-01-01T00:00:00.000Z",
      "created_at": "2026-01-01T00:00:00.000Z",
      "updated_at": "2026-01-01T00:00:00.000Z",
      "local_timezone": "text",
      "artifacts": {
        "transcript": "pending",
        "note": "pending",
        "summary": "pending",
        "codes": "pending"
      }
    }
  ],
  "has_more": true,
  "continuation_token": null
}

Create Session

post
Path parameters
workspace_idstring · uuidRequired
Body
external_appointment_idstring · max: 512 · nullableOptional
external_idstring · max: 512 · nullableOptional
modestring · enumOptionalDefault: in_personPossible values:
first_namestring · max: 256 · nullableOptional
last_namestring · max: 256 · nullableOptional
visit_typestring · enum · nullableOptionalPossible values:
note_templatestring · enum · nullableOptionalPossible values:
timezonestring · enum · min: 1RequiredPossible values:
Responses
201

Successful Response

application/json
idstring · uuidRequired
statusstring · enumRequiredPossible values:
modestring · enumRequired

Modality of a scribe session: an in-person ("mic") recording or a Zoom meeting.

Possible values:
external_appointment_idstring · max: 512 · nullableOptional
first_namestring · max: 256 · nullableOptional
last_namestring · max: 256 · nullableOptional
visit_typestring · max: 256 · nullableOptional
note_templatestring · enum · nullableOptionalPossible values:
started_atstring · date-time · nullableRequired
ended_atstring · date-time · nullableRequired
created_atstring · date-timeRequired
updated_atstring · date-timeRequired
local_timezonestring · max: 64Required
post/v1/{workspace_id}/sessions
POST /v1/{workspace_id}/sessions HTTP/1.1
Content-Type: application/json
Accept: */*
Content-Length: 254

{
  "external_appointment_id": "text",
  "external_id": "text",
  "mode": "in_person",
  "metadata": {
    "ANY_ADDITIONAL_PROPERTY": "anything"
  },
  "first_name": "text",
  "last_name": "text",
  "visit_type": "psych-intake",
  "note_template": "amd-psych-intake",
  "timezone": "Africa/Abidjan"
}
{
  "id": "123e4567-e89b-12d3-a456-426614174000",
  "status": "in-progress",
  "mode": "in_person",
  "external_appointment_id": "text",
  "first_name": "text",
  "last_name": "text",
  "visit_type": "text",
  "note_template": "amd-psych-intake",
  "started_at": "2026-01-01T00:00:00.000Z",
  "ended_at": "2026-01-01T00:00:00.000Z",
  "created_at": "2026-01-01T00:00:00.000Z",
  "updated_at": "2026-01-01T00:00:00.000Z",
  "local_timezone": "text",
  "artifacts": {
    "transcript": "pending",
    "note": "pending",
    "summary": "pending",
    "codes": "pending"
  }
}

Get Session

get
Path parameters
workspace_idstring · uuidRequired
session_idstring · uuidRequired
Responses
200

Successful Response

application/json
idstring · uuidRequired
statusstring · enumRequiredPossible values:
modestring · enumRequired

Modality of a scribe session: an in-person ("mic") recording or a Zoom meeting.

Possible values:
external_appointment_idstring · max: 512 · nullableOptional
first_namestring · max: 256 · nullableOptional
last_namestring · max: 256 · nullableOptional
visit_typestring · max: 256 · nullableOptional
note_templatestring · enum · nullableOptionalPossible values:
started_atstring · date-time · nullableRequired
ended_atstring · date-time · nullableRequired
created_atstring · date-timeRequired
updated_atstring · date-timeRequired
local_timezonestring · max: 64Required
get/v1/{workspace_id}/sessions/{session_id}
GET /v1/{workspace_id}/sessions/{session_id} HTTP/1.1
Accept: */*
{
  "id": "123e4567-e89b-12d3-a456-426614174000",
  "status": "in-progress",
  "mode": "in_person",
  "external_appointment_id": "text",
  "first_name": "text",
  "last_name": "text",
  "visit_type": "text",
  "note_template": "amd-psych-intake",
  "started_at": "2026-01-01T00:00:00.000Z",
  "ended_at": "2026-01-01T00:00:00.000Z",
  "created_at": "2026-01-01T00:00:00.000Z",
  "updated_at": "2026-01-01T00:00:00.000Z",
  "local_timezone": "text",
  "artifacts": {
    "transcript": "pending",
    "note": "pending",
    "summary": "pending",
    "codes": "pending"
  }
}

Update Session

patch

Patch an owned session's mutable fields (external_appointment_id / metadata).

Idempotent; only fields present in the body are written (sending external_appointment_id: null clears the link). A session's mode is fixed at create and is not a field on UpdateSessionRequest, so PATCH cannot flip a session to zoom (that would mint a bot-less Zoom session that never went through the saga at POST /zoom/sessions — the 5dca2633 failure class). Returns the full refreshed session so callers see accurate artifact availability.

Path parameters
workspace_idstring · uuidRequired
session_idstring · uuidRequired
Body

Mutable fields for PATCH /sessions/{id}. Only fields present in the body are updated.

A session's mode is fixed at create — it is deliberately NOT a field here, so PATCH cannot flip a session to zoom (which would mint a bot-less Zoom session that never went through the saga — the 5dca2633 failure class). A Zoom session is created via POST /v1/{workspace_id}/zoom/sessions. external_appointment_id is explicitly nullable — sending null clears the link — so the route distinguishes "field omitted" (leave as-is) from "field set to null" via exclude_unset.

external_appointment_idstring · max: 512 · nullableOptional
first_namestring · max: 256 · nullableOptional
last_namestring · max: 256 · nullableOptional
visit_typestring · enum · nullableOptionalPossible values:
note_templatestring · enum · nullableOptionalPossible values:
Responses
200

Successful Response

application/json
idstring · uuidRequired
statusstring · enumRequiredPossible values:
modestring · enumRequired

Modality of a scribe session: an in-person ("mic") recording or a Zoom meeting.

Possible values:
external_appointment_idstring · max: 512 · nullableOptional
first_namestring · max: 256 · nullableOptional
last_namestring · max: 256 · nullableOptional
visit_typestring · max: 256 · nullableOptional
note_templatestring · enum · nullableOptionalPossible values:
started_atstring · date-time · nullableRequired
ended_atstring · date-time · nullableRequired
created_atstring · date-timeRequired
updated_atstring · date-timeRequired
local_timezonestring · max: 64Required
patch/v1/{workspace_id}/sessions/{session_id}
PATCH /v1/{workspace_id}/sessions/{session_id} HTTP/1.1
Content-Type: application/json
Accept: */*
Content-Length: 186

{
  "external_appointment_id": "text",
  "metadata": {
    "ANY_ADDITIONAL_PROPERTY": "anything"
  },
  "first_name": "text",
  "last_name": "text",
  "visit_type": "psych-intake",
  "note_template": "amd-psych-intake"
}
{
  "id": "123e4567-e89b-12d3-a456-426614174000",
  "status": "in-progress",
  "mode": "in_person",
  "external_appointment_id": "text",
  "first_name": "text",
  "last_name": "text",
  "visit_type": "text",
  "note_template": "amd-psych-intake",
  "started_at": "2026-01-01T00:00:00.000Z",
  "ended_at": "2026-01-01T00:00:00.000Z",
  "created_at": "2026-01-01T00:00:00.000Z",
  "updated_at": "2026-01-01T00:00:00.000Z",
  "local_timezone": "text",
  "artifacts": {
    "transcript": "pending",
    "note": "pending",
    "summary": "pending",
    "codes": "pending"
  }
}

Get Actions

get

Reload-safe action-items poller (phase 09): the same job-state contract as the other artifacts.

Path parameters
workspace_idstring · uuidRequired
session_idstring · uuidRequired
Responses
200

Successful Response

application/json
generation_statusstring · enumRequiredPossible values:
session_idstring · uuid · nullableOptional
get/v1/{workspace_id}/sessions/{session_id}/actions
GET /v1/{workspace_id}/sessions/{session_id}/actions HTTP/1.1
Accept: */*
{
  "generation_status": "ready",
  "error": {
    "field": "text",
    "message": "text"
  },
  "session_id": "123e4567-e89b-12d3-a456-426614174000",
  "items": [
    {
      "id": "text",
      "text": "text",
      "kind": "text"
    }
  ]
}

Generate Actions

post

Enqueue post-visit action-item generation for an owned session (explicit regenerate, phase 09).

Async ensure-semantics identical to codes/summary: derives the idempotency identity from the canonical transcript + latest note (no request body), idempotently enqueues a scribe.generations job (artifact_kind='actions'), and returns 202 with the job envelope or 200 with the artifact when an identical job already succeeded. Actions are also auto-enqueued server-side when the note job succeeds. Rejected once the session is terminal (409 invalid_session_state, post-finalize).

Path parameters
workspace_idstring · uuidRequired
session_idstring · uuidRequired
Responses
200

An identical job already succeeded; the actions are returned.

application/json
post/v1/{workspace_id}/sessions/{session_id}/actions
POST /v1/{workspace_id}/sessions/{session_id}/actions HTTP/1.1
Accept: */*
{
  "actions": {
    "session_id": "123e4567-e89b-12d3-a456-426614174000",
    "items": [
      {
        "id": "text",
        "text": "text",
        "kind": "text"
      }
    ]
  },
  "generation": {
    "id": "123e4567-e89b-12d3-a456-426614174000",
    "model_provider": "text",
    "model_name": "text",
    "prompt_version": "text",
    "generated_at": "2026-01-01T00:00:00.000Z"
  }
}

Allocate Session

post

Allocate a scribe-streaming GameServer for an owned session and return {host, expires_at}.

The dispatcher leg of create -> allocate -> WS-attach (decision 3): the browser never allocates. The allocation is intentionally NOT persisted as a session mutation — a reconnect re-allocates a fresh GameServer, and the WS attach re-verifies ownership.

Path parameters
workspace_idstring · uuidRequired
session_idstring · uuidRequired
Responses
200

Successful Response

application/json
hoststring · max: 253Required
expires_atstring · date-timeRequired
post/v1/{workspace_id}/sessions/{session_id}/allocate
POST /v1/{workspace_id}/sessions/{session_id}/allocate HTTP/1.1
Accept: */*
{
  "host": "text",
  "expires_at": "2026-01-01T00:00:00.000Z"
}

Ask Session

post

Streaming Q&A over the session transcript + latest note (SPEC §3.3, header-auth fetch streaming).

Not persisted as an artifact — only a provenance row is recorded (its id is the generation_id in the terminal done frame). Reuses generation.py's provider clients. Allowed on a terminal session (post-visit Q&A is read-only; the post-finalize immutability guard applies only to regenerate-section + actions generation).

Path parameters
workspace_idstring · uuidRequired
session_idstring · uuidRequired
Body

Body of POST /sessions/{id}/ask — a streaming Q&A over the transcript + latest note.

The answer streams back as SSE delta {text} frames then a terminal done {generation_id}; the response is not persisted as an artifact (only a provenance row is recorded).

questionstring · min: 1 · max: 4000Required
Responses
200

A Server-Sent Event stream (header-authenticated fetch streaming, not EventSource): one or more delta frames each {text}, then a terminal done frame {generation_id}; a ping keepalive every 15 s, and an error frame if generation fails mid-stream. Each frame is event: <event>\ndata: <json>\n\n.

anyOptional
post/v1/{workspace_id}/sessions/{session_id}/ask
POST /v1/{workspace_id}/sessions/{session_id}/ask HTTP/1.1
Content-Type: application/json
Accept: */*
Content-Length: 61

{
  "question": "text",
  "history": [
    {
      "role": "user",
      "text": "text"
    }
  ]
}

No content

Cancel Session

post

Cancel an owned non-terminal session (-> cancelled); the saga-compensation / abort path.

Guarded so a terminal session no-ops with 409 invalid_session_state. Touches only status, never transcript artifacts.

Path parameters
workspace_idstring · uuidRequired
session_idstring · uuidRequired
Responses
200

Successful Response

application/json
idstring · uuidRequired
statusstring · enumRequiredPossible values:
modestring · enumRequired

Modality of a scribe session: an in-person ("mic") recording or a Zoom meeting.

Possible values:
external_appointment_idstring · max: 512 · nullableOptional
first_namestring · max: 256 · nullableOptional
last_namestring · max: 256 · nullableOptional
visit_typestring · max: 256 · nullableOptional
note_templatestring · enum · nullableOptionalPossible values:
started_atstring · date-time · nullableRequired
ended_atstring · date-time · nullableRequired
created_atstring · date-timeRequired
updated_atstring · date-timeRequired
local_timezonestring · max: 64Required
post/v1/{workspace_id}/sessions/{session_id}/cancel
POST /v1/{workspace_id}/sessions/{session_id}/cancel HTTP/1.1
Accept: */*
{
  "id": "123e4567-e89b-12d3-a456-426614174000",
  "status": "in-progress",
  "mode": "in_person",
  "external_appointment_id": "text",
  "first_name": "text",
  "last_name": "text",
  "visit_type": "text",
  "note_template": "amd-psych-intake",
  "started_at": "2026-01-01T00:00:00.000Z",
  "ended_at": "2026-01-01T00:00:00.000Z",
  "created_at": "2026-01-01T00:00:00.000Z",
  "updated_at": "2026-01-01T00:00:00.000Z",
  "local_timezone": "text",
  "artifacts": {
    "transcript": "pending",
    "note": "pending",
    "summary": "pending",
    "codes": "pending"
  }
}

Get Checklist

get

Read the session's checklist.

The checklist is instantiated from the session's visit type when the session is created and is the authoritative source of its items, so this returns them from the first frame — including before any transcript exists. Persisted per-item manual and auto toggle state is overlaid on the items. Returns 404 when the session has no checklist.

Path parameters
workspace_idstring · uuidRequired
session_idstring · uuidRequired
Responses
200

Successful Response

application/json
generation_statusstring · enumRequiredPossible values:
session_idstring · uuid · nullableOptional
titlestring · max: 256 · nullableOptional
statusstring · enum · nullableOptionalPossible values:
created_atstring · date-time · nullableOptional
updated_atstring · date-time · nullableOptional
get/v1/{workspace_id}/sessions/{session_id}/checklist
GET /v1/{workspace_id}/sessions/{session_id}/checklist HTTP/1.1
Accept: */*
{
  "generation_status": "ready",
  "error": {
    "field": "text",
    "message": "text"
  },
  "session_id": "123e4567-e89b-12d3-a456-426614174000",
  "title": "text",
  "status": "open",
  "items": [
    {
      "id": "text",
      "label": "text",
      "category": "text",
      "state": "open",
      "evidence": "text",
      "source": "manual",
      "updated_by": "123e4567-e89b-12d3-a456-426614174000",
      "updated_at": "2026-01-01T00:00:00.000Z"
    }
  ],
  "created_at": "2026-01-01T00:00:00.000Z",
  "updated_at": "2026-01-01T00:00:00.000Z"
}

Update Checklist

patch

Merge manual per-item toggles into the checklist state, each provenanced (who/when, source).

Requires a generated checklist (404 otherwise); every toggled id must belong to it (422 per-field otherwise). Toggles are idempotent upserts, re-toggleable until finalize; 409 invalid_session_state once the session is terminal. Auto-check state (phase 09) is written server-side, never here.

Path parameters
workspace_idstring · uuidRequired
session_idstring · uuidRequired
Body

Body of PATCH /sessions/{id}/checklist (V340): manual per-item toggles.

Responses
200

Successful Response

application/json

Response of PATCH /sessions/{id}/checklist: the generated checklist with per-item manual state + provenance overlaid, and status recomputed from the effective item states.

session_idstring · uuidRequired
titlestring · max: 256Required
statusstring · enumRequiredPossible values:
updated_atstring · date-timeRequired
patch/v1/{workspace_id}/sessions/{session_id}/checklist
PATCH /v1/{workspace_id}/sessions/{session_id}/checklist HTTP/1.1
Content-Type: application/json
Accept: */*
Content-Length: 60

{
  "items": [
    {
      "id": "text",
      "completed": true,
      "source": "manual"
    }
  ]
}
{
  "session_id": "123e4567-e89b-12d3-a456-426614174000",
  "title": "text",
  "status": "open",
  "items": [
    {
      "id": "text",
      "label": "text",
      "category": "text",
      "state": "open",
      "evidence": "text",
      "source": "manual",
      "updated_by": "123e4567-e89b-12d3-a456-426614174000",
      "updated_at": "2026-01-01T00:00:00.000Z"
    }
  ],
  "updated_at": "2026-01-01T00:00:00.000Z"
}

Auto Check Checklist

post

LLM auto-check of the generated checklist against the (partial) transcript mid-session (phase 09).

Evaluates each generated checklist item against the best-available transcript and returns a per-item matched verdict; matched items are persisted as source='auto' checklist state that COEXISTS with manual PATCH /checklist toggles — the upsert preserves any manually-decided item and never clobbers it. No generated checklist yet (or no transcript) → an empty {matches:[]}.

No-op guard (phase 88): the handler hashes the auto-check input (selected transcript + checklist items) and compares it to the last-checked hash stored on the session. An unchanged input short-circuits — no LLM call, no upsert — and returns the already-persisted source='auto' state; a changed (or first) input runs the LLM as before and then, under the session row lock, persists both the matched items and the new hash. This keeps a redundant re-check (client double-fire, reconnect, or a fire ahead of a fresher server snapshot) cheap regardless of the client's cadence.

Path parameters
workspace_idstring · uuidRequired
session_idstring · uuidRequired
Responses
200

Successful Response

application/json

200 body of POST /sessions/{id}/checklist/auto-check: the LLM's per-item match verdict.

Matched items are also persisted server-side as source='auto' checklist state (coexisting with, and never clobbering, manual PATCH /checklist toggles).

post/v1/{workspace_id}/sessions/{session_id}/checklist/auto-check
POST /v1/{workspace_id}/sessions/{session_id}/checklist/auto-check HTTP/1.1
Accept: */*
{
  "matches": [
    {
      "item_id": "text",
      "category": "text",
      "matched": true,
      "evidence": "text"
    }
  ]
}

Get Codes

get

Reload-safe read of the session's ICD codes (phase 130: session-scoped, not generation-anchored).

Returns EVERY code owned by the caller on this session — both AI suggestions and provider-authored rows — so provider codes are visible and the read no longer 404s when no codes generation ever ran. generation_status is advisory metadata about the AI generation only (ready when none ran or it succeeded, pending while running, failed with its error): the items are authoritative regardless.

Path parameters
workspace_idstring · uuidRequired
session_idstring · uuidRequired
Responses
200

Successful Response

application/json
generation_statusstring · enumRequiredPossible values:
session_idstring · uuid · nullableOptional
get/v1/{workspace_id}/sessions/{session_id}/codes
GET /v1/{workspace_id}/sessions/{session_id}/codes HTTP/1.1
Accept: */*
{
  "generation_status": "ready",
  "error": {
    "field": "text",
    "message": "text"
  },
  "session_id": "123e4567-e89b-12d3-a456-426614174000",
  "items": [
    {
      "id": "123e4567-e89b-12d3-a456-426614174000",
      "code": "text",
      "description": "text",
      "rationale": "text",
      "confidence": 1,
      "decision": "approved",
      "source": "ai"
    }
  ]
}

Generate Codes

post

Enqueue ICD-10-CM code-suggestion generation for an owned session (explicit regenerate).

Async ensure-semantics: derives the idempotency identity from the canonical transcript + latest note (no request body), then idempotently enqueues a scribe.generations job (artifact_kind='codes'). The worker prompts the model, parses strict-JSON suggestions, and persists one scribe.icd_suggestions row per code (an empty extraction is treated as a failed generation). Returns 202 with the job envelope, or 200 with the codes if an identical job already succeeded. Codes are also auto-enqueued server-side when the note job succeeds.

Path parameters
workspace_idstring · uuidRequired
session_idstring · uuidRequired
Responses
200

An identical job already succeeded; the codes are returned.

application/json
post/v1/{workspace_id}/sessions/{session_id}/codes
POST /v1/{workspace_id}/sessions/{session_id}/codes HTTP/1.1
Accept: */*
{
  "codes": {
    "session_id": "123e4567-e89b-12d3-a456-426614174000",
    "items": [
      {
        "id": "123e4567-e89b-12d3-a456-426614174000",
        "code": "text",
        "description": "text",
        "rationale": "text",
        "confidence": 1,
        "decision": "approved",
        "source": "ai"
      }
    ]
  },
  "generation": {
    "id": "123e4567-e89b-12d3-a456-426614174000",
    "model_provider": "text",
    "model_name": "text",
    "prompt_version": "text",
    "generated_at": "2026-01-01T00:00:00.000Z"
  }
}

Create Code

post

Add a provider-authored ICD-10 code to an owned session (phase 130).

Persists the code with source='provider', generation_id=NULL, and decision='approved' (adding a code is an affirmative act → it counts toward the finalize diagnosis gate immediately), owned by the caller. Does NOT require a codes generation to exist. 409 invalid_session_state once the session is terminal (post-finalize immutable); scoped scribe:notes:rw_own.

Path parameters
workspace_idstring · uuidRequired
session_idstring · uuidRequired
Body

Body of POST /sessions/{id}/codes/manual (phase 130): a provider-authored ICD code.

Persisted with source='provider', generation_id=NULL, and decision='approved' by default (adding a code is an affirmative act → it counts toward the finalize diagnosis gate immediately). Does not require a codes generation to exist.

codestring · min: 1 · max: 64Required
descriptionstring · min: 1 · max: 2000Required
rationalestring · max: 10000OptionalDefault: ""
Responses
201

Successful Response

application/json
idstring · uuidRequired
codestring · max: 64Required
descriptionstring · max: 2000Required
rationalestring · max: 10000Required
confidencenumber · max: 1 · nullableOptional
decisionstring · enum · nullableOptionalPossible values:
sourcestring · enumRequiredPossible values:
post/v1/{workspace_id}/sessions/{session_id}/codes/manual
POST /v1/{workspace_id}/sessions/{session_id}/codes/manual HTTP/1.1
Content-Type: application/json
Accept: */*
Content-Length: 51

{
  "code": "text",
  "description": "text",
  "rationale": ""
}
{
  "id": "123e4567-e89b-12d3-a456-426614174000",
  "code": "text",
  "description": "text",
  "rationale": "text",
  "confidence": 1,
  "decision": "approved",
  "source": "ai"
}

Decide Code

patch

Record a provider's approve/reject decision and/or edit the text of one ICD suggestion.

Decision (approve/reject) applies to any owned row (SPEC §6.1 P4; idempotent, re-decidable until finalize). Text edits (code/description/rationale, phase 130) are honored ONLY for provider-authored rows — editing an AI suggestion's text is rejected with 422 invalid_code_edit (AI codes stay approve/reject; reject + add a provider code to change one). 409 invalid_session_state once the session is terminal. 404 when the suggestion is unknown or not owned by the provider.

Path parameters
workspace_idstring · uuidRequired
session_idstring · uuidRequired
suggestion_idstring · uuidRequired
Body

Body of PATCH /sessions/{id}/codes/{suggestion_id}.

Carries the provider's per-suggestion decision (V340; idempotent, re-decidable until finalize) and, since phase 130, optional text edits (code/description/rationale). Text edits are honored only for provider-authored rows (AI suggestions stay approve/reject). At least one field must be present.

decisionstring · enum · nullableOptionalPossible values:
codestring · min: 1 · max: 64 · nullableOptional
descriptionstring · min: 1 · max: 2000 · nullableOptional
rationalestring · max: 10000 · nullableOptional
Responses
200

Successful Response

application/json

200 body for a persisted code decision/edit, with when it was decided (who is the caller).

idstring · uuidRequired
codestring · max: 64Required
decisionstring · enumRequiredPossible values:
decided_atstring · date-timeRequired
sourcestring · enumRequiredPossible values:
patch/v1/{workspace_id}/sessions/{session_id}/codes/{suggestion_id}
PATCH /v1/{workspace_id}/sessions/{session_id}/codes/{suggestion_id} HTTP/1.1
Content-Type: application/json
Accept: */*
Content-Length: 77

{
  "decision": "approved",
  "code": "text",
  "description": "text",
  "rationale": "text"
}
{
  "id": "123e4567-e89b-12d3-a456-426614174000",
  "code": "text",
  "decision": "approved",
  "decided_at": "2026-01-01T00:00:00.000Z",
  "source": "ai"
}

End Session

post

End an owned session (created/in-progress -> in-review), guarded against a live stream.

Mic race guard (§3A.2): while a streaming worker is attached the WS worker is the sole finalizer, so REST end returns 409 session_streaming and never touches transcript artifacts. Permitted only for an abandoned/never-streamed session; the guarded flip cannot race the worker or reaper.

Path parameters
workspace_idstring · uuidRequired
session_idstring · uuidRequired
Responses
200

Successful Response

application/json
idstring · uuidRequired
statusstring · enumRequiredPossible values:
modestring · enumRequired

Modality of a scribe session: an in-person ("mic") recording or a Zoom meeting.

Possible values:
external_appointment_idstring · max: 512 · nullableOptional
first_namestring · max: 256 · nullableOptional
last_namestring · max: 256 · nullableOptional
visit_typestring · max: 256 · nullableOptional
note_templatestring · enum · nullableOptionalPossible values:
started_atstring · date-time · nullableRequired
ended_atstring · date-time · nullableRequired
created_atstring · date-timeRequired
updated_atstring · date-timeRequired
local_timezonestring · max: 64Required
post/v1/{workspace_id}/sessions/{session_id}/end
POST /v1/{workspace_id}/sessions/{session_id}/end HTTP/1.1
Accept: */*
{
  "id": "123e4567-e89b-12d3-a456-426614174000",
  "status": "in-progress",
  "mode": "in_person",
  "external_appointment_id": "text",
  "first_name": "text",
  "last_name": "text",
  "visit_type": "text",
  "note_template": "amd-psych-intake",
  "started_at": "2026-01-01T00:00:00.000Z",
  "ended_at": "2026-01-01T00:00:00.000Z",
  "created_at": "2026-01-01T00:00:00.000Z",
  "updated_at": "2026-01-01T00:00:00.000Z",
  "local_timezone": "text",
  "artifacts": {
    "transcript": "pending",
    "note": "pending",
    "summary": "pending",
    "codes": "pending"
  }
}

Stream Zoom Session Events

get

Bridge the Zoom control plane's event stream to the browser as SSE, under provider-JWT auth.

Resolves the owned, live Zoom session to its bound bot, then relays the control plane's Redis-backed event stream (bot_status / transcript frames) with a 15 s ping keepalive, forwarding Last-Event-ID upstream for replay and closing cleanly after a terminal bot_status. 404 for a non-Zoom, unknown, terminal, or bot-less session. The browser never talks to the control plane and holds only its own JWT.

Path parameters
workspace_idstring · uuidRequired
session_idstring · uuidRequired
Header parameters
Last-Event-IDstring · nullableOptional
Responses
200

A live Server-Sent Event stream (header-authenticated fetch streaming, not EventSource). Emits bot_status, transcript_segment / interim_transcript, and transcript_finalized frames plus a ping keepalive every 15 s; replays recent events on reconnect via Last-Event-ID and closes after a terminal bot_status. Each frame is event: <event>\ndata: <json>\n\n.

One frame on the GET /sessions/{id}/events SSE stream (documents the wire contract for the SDK).

Each frame is delivered as event: <event>\ndata: <json>\n\n; ping carries an empty {} body and is a keepalive only. The stream is header-authenticated fetch streaming (not EventSource), replays recent events on reconnect via Last-Event-ID, and closes after a terminal bot_status.

eventstring · enumRequiredPossible values:
dataany ofRequired
or
or
object · TranscriptFinalizedEventOptional

transcript_finalized SSE frame payload: emitted once, with no body, when the transcript is done.

or
get/v1/{workspace_id}/sessions/{session_id}/events
GET /v1/{workspace_id}/sessions/{session_id}/events HTTP/1.1
Accept: */*
{
  "event": "bot_status",
  "data": {
    "state": "joining",
    "reason": "text"
  }
}

Get Note

get
Path parameters
workspace_idstring · uuidRequired
session_idstring · uuidRequired
Responses
200

Successful Response

application/json

Reload-safe note poller: artifact fields present only when generation_status == "ready".

generation_status == "empty" is the phase-147 terminal state for a session ended with zero usable transcript segments: no note was (or will be) generated, and no artifact fields are present.

generation_statusstring · enumRequiredPossible values:
writeback_statusstring · enumRequiredPossible values:
session_idstring · uuid · nullableOptional
typestring · max: 64 · nullableOptional
statusstring · enum · nullableOptionalPossible values:
bodystring · max: 1000000 · nullableOptionalDeprecated
versioninteger · min: 1 · nullableOptional
generated_atstring · date-time · nullableOptional
signed_atstring · date-time · nullableOptional
updated_atstring · date-time · nullableOptional
clinician_namestring · nullableOptional
get/v1/{workspace_id}/sessions/{session_id}/note
GET /v1/{workspace_id}/sessions/{session_id}/note HTTP/1.1
Accept: */*
{
  "generation_status": "ready",
  "writeback_status": "succeeded",
  "error": {
    "field": "text",
    "message": "text"
  },
  "session_id": "123e4567-e89b-12d3-a456-426614174000",
  "type": "text",
  "status": "draft",
  "structured": {
    "ANY_ADDITIONAL_PROPERTY": "anything"
  },
  "version": 1,
  "generated_at": "2026-01-01T00:00:00.000Z",
  "signed_at": "2026-01-01T00:00:00.000Z",
  "updated_at": "2026-01-01T00:00:00.000Z",
  "carry_forward_available": [
    {
      "amd_field_code": "text",
      "label": "text",
      "value": "text",
      "source_note_id": 1,
      "source_signed_at": "text"
    }
  ],
  "clinician_name": "text"
}

Generate Note

post
Path parameters
workspace_idstring · uuidRequired
session_idstring · uuidRequired
Body
note_typestring · enum · nullableOptionalPossible values:
instructionsstring · max: 1200 · nullableOptional
Responses
200

An identical job already succeeded; the note is returned.

application/json
post/v1/{workspace_id}/sessions/{session_id}/note
POST /v1/{workspace_id}/sessions/{session_id}/note HTTP/1.1
Content-Type: application/json
Accept: */*
Content-Length: 54

{
  "note_type": "amd-psych-intake",
  "instructions": "text"
}
{
  "note": {
    "session_id": "123e4567-e89b-12d3-a456-426614174000",
    "type": "text",
    "status": "draft",
    "structured": {
      "ANY_ADDITIONAL_PROPERTY": "anything"
    },
    "version": 1,
    "generated_at": "2026-01-01T00:00:00.000Z",
    "signed_at": "2026-01-01T00:00:00.000Z",
    "updated_at": "2026-01-01T00:00:00.000Z"
  },
  "generation": {
    "id": "123e4567-e89b-12d3-a456-426614174000",
    "model_provider": "text",
    "model_name": "text",
    "prompt_version": "text",
    "generated_at": "2026-01-01T00:00:00.000Z"
  }
}

Update Note

put

Versioned structured-note autosave with optimistic concurrency (design §4.2).

Accepts {base_version, structured} only: a complete structured-document replacement, validated as a full document against the pinned template (phase-71 registry) BEFORE the compare-and-set. The CAS on version means two racing writers with the same base_version yield exactly one success and one 409 version_conflict. Rejected once the session is terminal (append-only, 409 invalid_session_state). 404 when no note has been generated yet. An unsupported template is rejected with 422 unsupported_note_template; a missing or invalid structured document with 422 validation_error carrying per-field details.

Path parameters
workspace_idstring · uuidRequired
session_idstring · uuidRequired
Body

Body of PUT /sessions/{id}/note — versioned structured-note autosave (design §4.2).

The write is a complete structured-document replacement: send the full structured envelope (every value) on each write, not a partial patch. base_version is the version the client last read; a stale value loses the compare-and-set and returns 409 version_conflict.

base_versioninteger · min: 1Required
Responses
200

Successful Response

application/json

200 body for a successful PUT /sessions/{id}/note: the new version + write time.

versioninteger · min: 1Required
updated_atstring · date-timeRequired
put/v1/{workspace_id}/sessions/{session_id}/note
PUT /v1/{workspace_id}/sessions/{session_id}/note HTTP/1.1
Content-Type: application/json
Accept: */*
Content-Length: 280

{
  "base_version": 1,
  "structured": {
    "schema_version": 1,
    "template_id": "text",
    "template_version": "text",
    "values": {
      "ANY_ADDITIONAL_PROPERTY": {
        "value": "text",
        "rationale": "text",
        "source": "scribe"
      }
    },
    "review": {
      "confirmed_sections": [
        "text"
      ],
      "acknowledgments": [
        "text"
      ],
      "amd_confirmed": false
    }
  }
}
{
  "version": 1,
  "updated_at": "2026-01-01T00:00:00.000Z"
}

Finalize Note

post

Server-authoritative finalize (SPEC §6.1 P4): atomically submit the note + complete the session.

Phase 178: finalize accepts ANY stored note shape — there is no note-content/structural validation gate. A note with missing required fields, no approved diagnosis, amd_confirmed=false, and/or a structurally-off document finalizes 200. The only non-validation mechanics still enforced are ownership/scope, base_version optimistic concurrency, already-submitted idempotency (200), and the atomic session completion. An already-submitted note is an idempotent 200. A terminal cancelled/failed session is 409 invalid_session_state and the note submit is rolled back with it (the two writes are one unit of work).

EHR writeback (ehr-note-writeback phase 07): with scribe.note-writeback-enabled on for the workspace, the note is POSTed to the EHR inside the same transaction, after the session completes and before commit. Any writeback failure is ehr_writeback_failed (500 when the request can't be built, 502 when the EHR call fails) and rolls both writes back (note stays draft); success commits a note_writebacks provenance row with them. The already-submitted short-circuit means a successful finalize never re-posts. A separate submit flag sets submitToAmd: off, the writeback is a dry run (stored + mapped by the EHR, not written to AMD).

Path parameters
workspace_idstring · uuidRequired
session_idstring · uuidRequired
Body

Body of POST /sessions/{id}/note/finalize (V340).

base_version is the note version the provider is finalizing; a stale value returns 409 version_conflict. Server-side gate validation runs on the stored document, not the request, so a client cannot bypass required-field gates.

base_versioninteger · min: 1Required
Responses
200

Successful Response

application/json
writeback_statusstring · enumRequiredPossible values:
post/v1/{workspace_id}/sessions/{session_id}/note/finalize
POST /v1/{workspace_id}/sessions/{session_id}/note/finalize HTTP/1.1
Content-Type: application/json
Accept: */*
Content-Length: 18

{
  "base_version": 1
}
{
  "note": {
    "session_id": "123e4567-e89b-12d3-a456-426614174000",
    "type": "text",
    "status": "draft",
    "structured": {
      "ANY_ADDITIONAL_PROPERTY": "anything"
    },
    "version": 1,
    "generated_at": "2026-01-01T00:00:00.000Z",
    "signed_at": "2026-01-01T00:00:00.000Z",
    "updated_at": "2026-01-01T00:00:00.000Z"
  },
  "writeback_status": "succeeded",
  "field_dispositions": [
    {
      "amd_field_code": "text",
      "label": "text",
      "disposition": "written",
      "reason": "written_from_transcript",
      "source": "transcript",
      "source_note_id": 1,
      "source_signed_at": "text",
      "source_template_name": "text",
      "display_value": "text"
    }
  ]
}

Regenerate Note Section

post

Regenerate a single section of the note as a new version (async ensure-semantics, phase 09).

Reuses the phase-08 note version for optimistic concurrency: a stale base_version (a manual edit or another regen landed since the client read) is rejected up front with 409 version_conflict, and the worker re-checks it under a compare-and-set at persist time so a race can never clobber a newer note. Enqueues an artifact_kind='note' job whose params carry section_id/base_version; the worker rewrites that section and persists base_version + 1. Returns 202 with the job envelope. Rejected once the session is terminal (409 invalid_session_state, post-finalize immutability).

Path parameters
workspace_idstring · uuidRequired
session_idstring · uuidRequired
Body

Body of POST /sessions/{id}/note/regenerate-section (SPEC §6.1 P4).

Regenerates a single named section of the current note. base_version is the note version the client last read; a stale value returns 409 version_conflict (never clobbers a racing manual edit).

section_idstring · min: 1 · max: 128Required
instructionsstring · max: 1200 · nullableOptional
base_versioninteger · min: 1Required
Responses
202

Successful Response

application/json

202 body for an enqueued (or collapsed-onto-in-flight) generation job.

post/v1/{workspace_id}/sessions/{session_id}/note/regenerate-section
POST /v1/{workspace_id}/sessions/{session_id}/note/regenerate-section HTTP/1.1
Content-Type: application/json
Accept: */*
Content-Length: 60

{
  "section_id": "text",
  "instructions": "text",
  "base_version": 1
}
{
  "generation": {
    "id": "123e4567-e89b-12d3-a456-426614174000",
    "artifact_kind": "note",
    "status": "pending"
  }
}

Get Summary

get
Path parameters
workspace_idstring · uuidRequired
session_idstring · uuidRequired
Responses
200

Successful Response

application/json
generation_statusstring · enumRequiredPossible values:
session_idstring · uuid · nullableOptional
summarystring · max: 10000 · nullableOptional
generated_atstring · date-time · nullableOptional
updated_atstring · date-time · nullableOptional
get/v1/{workspace_id}/sessions/{session_id}/summary
GET /v1/{workspace_id}/sessions/{session_id}/summary HTTP/1.1
Accept: */*
{
  "generation_status": "ready",
  "error": {
    "field": "text",
    "message": "text"
  },
  "session_id": "123e4567-e89b-12d3-a456-426614174000",
  "summary": "text",
  "generated_at": "2026-01-01T00:00:00.000Z",
  "updated_at": "2026-01-01T00:00:00.000Z"
}

Generate Summary

post
Path parameters
workspace_idstring · uuidRequired
session_idstring · uuidRequired
Responses
200

An identical job already succeeded; the summary is returned.

application/json
post/v1/{workspace_id}/sessions/{session_id}/summary
POST /v1/{workspace_id}/sessions/{session_id}/summary HTTP/1.1
Accept: */*
{
  "summary": {
    "session_id": "123e4567-e89b-12d3-a456-426614174000",
    "summary": "text",
    "generated_at": "2026-01-01T00:00:00.000Z",
    "updated_at": "2026-01-01T00:00:00.000Z"
  },
  "generation": {
    "id": "123e4567-e89b-12d3-a456-426614174000",
    "model_provider": "text",
    "model_name": "text",
    "prompt_version": "text",
    "generated_at": "2026-01-01T00:00:00.000Z"
  }
}

Mint Attach Ticket

post

Mint a WS attach ticket for an owned, non-terminal session and return {ticket, expires_at}.

A thin server-side wrapper over identity's RFC 8693 token_exchange grant (the mint lives in identity): it forwards the browser's raw provider JWT as subject_token so identity binds the ticket to this session/workspace/provider (aud=scribe-streaming, scope scribe:streams:connect, ~5-min TTL). Routing this through the Scribe API keeps the browser's CORS surface a single service — CORS can't scope identity's /token to one grant type. The SDK connectionProvider calls this on every (re)connect and presents the ticket at the streaming worker's WS handshake.

Path parameters
workspace_idstring · uuidRequired
session_idstring · uuidRequired
Responses
200

Successful Response

application/json
ticketstringRequired
expires_atstring · date-timeRequired
post/v1/{workspace_id}/sessions/{session_id}/ticket
POST /v1/{workspace_id}/sessions/{session_id}/ticket HTTP/1.1
Accept: */*
{
  "ticket": "text",
  "expires_at": "2026-01-01T00:00:00.000Z"
}

Get Transcript

get
Path parameters
workspace_idstring · uuidRequired
session_idstring · uuidRequired
Responses
200

Successful Response

application/json
session_idstring · uuidRequired
get/v1/{workspace_id}/sessions/{session_id}/transcript
GET /v1/{workspace_id}/sessions/{session_id}/transcript HTTP/1.1
Accept: */*
{
  "session_id": "123e4567-e89b-12d3-a456-426614174000",
  "segments": [
    {
      "speaker": "text",
      "text": "text",
      "start_ms": 1,
      "end_ms": 1
    }
  ]
}

End Zoom Session

delete

End a Zoom session: 202 immediately, then drain the bot + internally finalize in the background.

Returns {status: "draining"} right away; the background task stops the bot and runs the internal zoom-finalize (promote the drained snapshot -> transcript.json, flip -> in-review) with a bounded retry over the drain window. Idempotent: a second DELETE still 202s and no-ops (the drain task sees the already-finalized session and skips promotion). 409 not_zoom_session for a mic session.

Path parameters
workspace_idstring · uuidRequired
session_idstring · uuidRequired
Responses
202

Successful Response

application/json

Response of DELETE /sessions/{id}/zoom: acknowledges that the drain + internal zoom-finalize is underway. The session flips to in-review asynchronously (reaper backstop).

statusconst: drainingRequired
delete/v1/{workspace_id}/sessions/{session_id}/zoom
DELETE /v1/{workspace_id}/sessions/{session_id}/zoom HTTP/1.1
Accept: */*
{
  "status": "text"
}

Pause Zoom Bot

post

Pause the Zoom capture bot bound to an owned session (proxies the control plane).

Path parameters
workspace_idstring · uuidRequired
session_idstring · uuidRequired
Responses
200

Successful Response

application/json

Response of POST /sessions/{id}/zoom/pause|resume: the bot's status after the proxied control-plane command. The live status transition also arrives over the event stream (phase 06); this is the synchronous ack.

bot_statusstring · max: 64Required
post/v1/{workspace_id}/sessions/{session_id}/zoom/pause
POST /v1/{workspace_id}/sessions/{session_id}/zoom/pause HTTP/1.1
Accept: */*
{
  "bot_status": "text"
}

Resume Zoom Bot

post

Resume the Zoom capture bot bound to an owned session (proxies the control plane).

Path parameters
workspace_idstring · uuidRequired
session_idstring · uuidRequired
Responses
200

Successful Response

application/json

Response of POST /sessions/{id}/zoom/pause|resume: the bot's status after the proxied control-plane command. The live status transition also arrives over the event stream (phase 06); this is the synchronous ack.

bot_statusstring · max: 64Required
post/v1/{workspace_id}/sessions/{session_id}/zoom/resume
POST /v1/{workspace_id}/sessions/{session_id}/zoom/resume HTTP/1.1
Accept: */*
{
  "bot_status": "text"
}

Delete Zoom Connection

delete

Disconnect Zoom: best-effort revoke at Zoom, delete the row. Idempotent -- deleting a non-existent connection still returns 204.

Path parameters
workspace_idstring · uuidRequired
Responses
204

Successful Response

No content

delete/v1/{workspace_id}/zoom/connection
DELETE /v1/{workspace_id}/zoom/connection HTTP/1.1
Accept: */*

No content

Get Zoom Connection

get

Whether the calling provider has a Zoom connection. NEVER returns token material.

Heals a near-expiry token on the poll (phase 35) so a connected-but-idle workspace never lets its Zoom token lapse — the refresh + persist happen server-side before the status read, which then reflects the outcome (reconnect_required is set when Zoom has rejected the stored credential).

Path parameters
workspace_idstring · uuidRequired
Responses
200

Successful Response

application/json

Response of GET /zoom/connection. NEVER carries token material.

Deliberately holds only a boolean plus non-sensitive display fields; the encrypted access/refresh tokens live only in scribe.zoom_connections and are resolved server-side (phase 05) to dispatch a bot, never returned to a browser.

reconnect_required is true when Zoom rejected the stored refresh token (revoked/ expired), so the poll heals into a terminal state the UX can act on: the provider is still connected on record, but must re-run the OAuth connect before a Zoom start will succeed.

connectedbooleanRequired
zoom_emailstring · max: 320 · nullableOptional
connected_atstring · date-time · nullableOptional
reconnect_requiredbooleanOptionalDefault: false
get/v1/{workspace_id}/zoom/connection
GET /v1/{workspace_id}/zoom/connection HTTP/1.1
Accept: */*
{
  "connected": true,
  "zoom_email": "text",
  "connected_at": "2026-01-01T00:00:00.000Z",
  "reconnect_required": false
}

Start Zoom Oauth

post

Begin the Zoom connect flow: mint a single-use server-held state + PKCE verifier bound to the provider/workspace and return the short-lived Zoom authorize URL the browser navigates to.

web_origin is the initiating web app's origin; if the code-level origin rule accepts it, it is threaded through the state so the callback can 302 the browser back to it (phase 55). A missing or rejected origin is dropped and the callback falls back to the configured return URL.

Path parameters
workspace_idstring · uuidRequired
Query parameters
web_originstring · max: 2048OptionalDefault: ""
Responses
200

Successful Response

application/json

Response of POST /zoom/oauth/authorize.

The browser navigates the top-level window to authorize_url (it cannot carry the in-memory Bearer header on a redirect, so the single-use server-held state embedded in the URL is the credential the unauthenticated callback trusts). expires_at is when that state's short TTL lapses.

authorize_urlstring · max: 2048Required
expires_atstring · date-timeRequired
post/v1/{workspace_id}/zoom/oauth/authorize
POST /v1/{workspace_id}/zoom/oauth/authorize HTTP/1.1
Accept: */*
{
  "authorize_url": "text",
  "expires_at": "2026-01-01T00:00:00.000Z"
}

Complete Zoom Oauth

get

Unauthenticated Zoom redirect target (NO Bearer -- the single-use state is the credential).

Validates + atomically consumes (GETDEL) the state, exchanges the code + PKCE verifier, and stores the encrypted tokens. Every failure -- invalid/expired/replayed state, missing code, failed exchange -- 302s to the web settings route with an error flag and renders no JSON to the human. Opted out of the provider-JWT dependency simply by omitting claims: ScribeClaimsParam.

Path parameters
workspace_idstring · uuidRequired
Query parameters
codestring · max: 2048OptionalDefault: ""
statestring · max: 512OptionalDefault: ""
Responses
302

Redirect to the web settings route with a zoom_connected=true flag on success or a zoom_error=<reason> flag on an invalid/expired/replayed state or a failed exchange.

No content

get/v1/{workspace_id}/zoom/oauth/callback
GET /v1/{workspace_id}/zoom/oauth/callback HTTP/1.1
Accept: */*

No content

Create Zoom Session

post

Run the whole Zoom capture saga server-side and return {session, bot_id} (201).

In order: validate the meeting link; friendly per-practitioner active-Zoom pre-check; resolve + decrypt the STORED Zoom token (409 zoom_not_connected if absent); create the session (born in-progress, mode=zoom, external_id idempotency); dispatch the bot via the control plane with the resolved token. On dispatch failure the session is CANCELLED and any straggler bot reaped (503 bot_dispatch_failed) so nothing is orphaned. A duplicate/idempotent create whose bot is already running recovers that bot instead of dispatching a second one.

Path parameters
workspace_idstring · uuidRequired
Body

Body of POST /zoom/sessions — the whole Zoom capture saga behind one call.

The browser hands only a meeting_link + disclosure choice (never a zoomAccessToken): the Scribe API creates the session, resolves the provider's STORED Zoom token, and dispatches the bot server-side. external_id is the per-attempt idempotency key (same semantics as CreateSessionRequest).

meeting_linkstring · uri · min: 1 · max: 2083Required
external_appointment_idstring · max: 512 · nullableOptional
external_idstring · max: 512 · nullableOptional
first_namestring · max: 256 · nullableOptional
last_namestring · max: 256 · nullableOptional
visit_typestring · enum · nullableOptionalPossible values:
note_templatestring · enum · nullableOptionalPossible values:
timezonestring · enum · min: 1RequiredPossible values:
Responses
201

Successful Response

application/json

Response of POST /zoom/sessions: the created (mode=zoom, in-progress) session plus the id of the dispatched control-plane bot the live stream is bound to.

bot_idstring · max: 256Required
post/v1/{workspace_id}/zoom/sessions
POST /v1/{workspace_id}/zoom/sessions HTTP/1.1
Content-Type: application/json
Accept: */*
Content-Length: 321

{
  "meeting_link": "https://example.com",
  "disclosure": {
    "enabled": true,
    "audio_key": "text"
  },
  "external_appointment_id": "text",
  "external_id": "text",
  "metadata": {
    "ANY_ADDITIONAL_PROPERTY": "anything"
  },
  "first_name": "text",
  "last_name": "text",
  "visit_type": "psych-intake",
  "note_template": "amd-psych-intake",
  "timezone": "Africa/Abidjan"
}
{
  "session": {
    "id": "123e4567-e89b-12d3-a456-426614174000",
    "status": "in-progress",
    "mode": "in_person",
    "external_appointment_id": "text",
    "first_name": "text",
    "last_name": "text",
    "visit_type": "text",
    "note_template": "amd-psych-intake",
    "started_at": "2026-01-01T00:00:00.000Z",
    "ended_at": "2026-01-01T00:00:00.000Z",
    "created_at": "2026-01-01T00:00:00.000Z",
    "updated_at": "2026-01-01T00:00:00.000Z",
    "local_timezone": "text",
    "artifacts": {
      "transcript": "pending",
      "note": "pending",
      "summary": "pending",
      "codes": "pending"
    }
  },
  "bot_id": "text"
}

Last updated

Was this helpful?