Sessions
List and retrieve provider-owned clinical documentation sessions with lifecycle state and transcript, note, summary, and coding artifact availability.
Sessions represent provider-owned clinical documentation encounters. Each session tracks its mode, lifecycle status, and artifact availability. Use the Scribe API base URL assigned to your deployment; these are not the Platform text-session WebSocket routes.
Read operations require scribe:sessions:read_own. Creating, updating, ending, cancelling, and controlling Zoom sessions require scribe:sessions:write, in addition to provider ownership and workspace access.
Session Object
Read the session schema in Get Session. Session lifecycle, capture mode, and individual artifact readiness describe different parts of the encounter.
Artifact Availability
The artifacts object reports readiness for each artifact. Inspect the artifact's own generation state before displaying a note or concluding that generation completed.
List Sessions
50Successful Response
Bearer token is absent or invalid.
The principal is outside this workspace or provider scope.
No provider-owned resource exists at this URL.
Generation cannot proceed for the current session state.
Request validation failed.
Clinical generation is temporarily unavailable.
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
}Returns a paginated list of sessions owned by the authenticated provider, ordered by creation time (newest first).
Query Parameters
Use the limit and continuation token in List Sessions; pass the returned continuation token unchanged for the next page.
Response
Use the session items and pagination fields rendered in List Sessions.
Example Request
Example Response
Get Session
Returns a single session owned by the authenticated provider.
Path Parameters
Use the matching workspace and the provider-owned session identifier in Get Session.
Response
Returns a session object (see schema above).
Errors
404
not_found
Session does not exist or is not owned by the authenticated provider
Create an In-Person Session
in_personPossible values: Successful Response
Modality of a scribe session: an in-person ("mic") recording or a Zoom meeting.
Bearer token is absent or invalid.
The principal is outside this workspace or provider scope.
external_id is already in use by another provider (conflict), or was reused with a different fingerprint (idempotency_key_conflict).
Request validation failed (validation_error); a non-in-person mode was requested (use_zoom_endpoint) — the generic create is in-person only, create a Zoom session via POST /v1/{workspace_id}/zoom/sessions; no canonical visit_type was supplied to resolve a supported amd-* template (note_template_required); or the visit_type/note_template does not map to a supported amd-* template (unsupported_note_template).
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"
}
}Create a provider-owned microphone session. The general create route accepts only in_person mode. Supply a canonical visit_type; the service resolves and pins the corresponding supported note template. A supplied note_template must match. A missing visit type returns 422 note_template_required. An unrecognized visit_type or note_template value fails request validation with 422 validation_error, and a note_template that does not match the visit type returns 422 unsupported_note_template. No session is created in either case. The former generic templates (full, medical, soap, dap, birp) and the medical visit type are no longer accepted.
Each supported visit type has a telehealth and an in-person template variant (the -ip identifiers). When the session links to an appointment whose modality is known, that modality selects the variant, whatever the capture method. Otherwise an in-person session gets the in-person variant and a Zoom session gets the telehealth one. A supplied note_template can be the resolved template or its telehealth base, and the service pins the variant it resolved. Read note_template on the returned session for the pinned value.
Send timezone on every create: the provider's IANA time zone name, such as America/New_York. It is required on this route and on Create a Zoom Session; a missing or unrecognized zone returns 422. The session's local_timezone records the zone used for its local times and dates, for example in a configured EHR writeback. It is the supplied timezone unless the session links to an appointment whose own time zone can be resolved. Clients generated from an older schema must add this field.
The request also supports patient display-name fields, an optional appointment reference, metadata, and external_id for idempotency. Inspect the assigned Scribe schema for field constraints. A successful create returns the session with HTTP 201. Recording attachment and artifact generation are separate operations.
Session Mode
Every session carries a mode that indicates the session modality:
in_person
An in-person microphone recording (default).
zoom
A Zoom meeting session. A practitioner may have at most one non-terminal Zoom session at a time within a workspace.
The mode is fixed at creation. The general create-session route accepts in_person; use POST /v1/{workspace_id}/zoom/sessions for a Zoom session. PATCH does not change the mode.
Session Lifecycle
A session is in-progress from creation; there is no separate created state. Sessions support explicit REST transitions in addition to streaming-driven state changes:
End
POST /v1/{workspace_id}/sessions/{session_id}/end
in-progress
in-review
Refused with 409 session_streaming while a streaming worker is attached.
Cancel
POST /v1/{workspace_id}/sessions/{session_id}/cancel
in-progress, in-review
cancelled
Compensation and abort path. Terminal sessions return 409 invalid_session_state.
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.
Successful Response
Modality of a scribe session: an in-person ("mic") recording or a Zoom meeting.
Bearer token is absent or invalid.
The principal is outside this workspace or provider scope.
No provider-owned resource exists at this URL.
A streaming worker is still attached (session_streaming) or the session is not in an endable state (invalid_session_state).
Validation Error
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"
}
}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.
Successful Response
Modality of a scribe session: an in-person ("mic") recording or a Zoom meeting.
Bearer token is absent or invalid.
The principal is outside this workspace or provider scope.
No provider-owned resource exists at this URL.
The session is already terminal and cannot be cancelled (invalid_session_state).
Validation Error
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"
}
}Update Session
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.
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.
Successful Response
Modality of a scribe session: an in-person ("mic") recording or a Zoom meeting.
Bearer token is absent or invalid.
The principal is outside this workspace or provider scope.
No provider-owned resource exists at this URL.
Request validation failed (validation_error); or a PATCH of visit_type/note_template would leave the session without a supported amd-* template — no resolvable visit_type (note_template_required) or an unrecognized/legacy/mismatched pair (unsupported_note_template).
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"
}
}Only fields present in the request body are written. Omission preserves a field; an accepted explicit null clears the corresponding nullable value. Mode remains fixed.
Use the operation above for accepted fields and constraints.
Returns the full refreshed session response. Changes to visit_type or note_template revalidate the supported pair. Changing the visit type also refreshes its checklist; that change is rejected for a terminal session.
Create Session Idempotency
When external_id is supplied on create, it acts as a per-attempt idempotency key:
Matching fingerprint: A repeated
external_idwith the same effective create parameters returns the existing session. Use the same request when retrying an ambiguous result.Divergent fingerprint: If the effective create parameters differ from the stored session, the endpoint returns
409with error codeidempotency_key_conflict. Generate a newexternal_idfor a deliberately new session.
Omitting external_id or sending null does not supply an idempotency key. Creation remains subject to permissions, validation, and the active-Zoom-session guard.
Conflict Error Codes
Session lifecycle and create endpoints return 409 responses with a stable machine-readable code in the error envelope:
idempotency_key_conflict
Create reused an external_id with a different request fingerprint.
active_zoom_session_exists
A second non-terminal Zoom session was attempted for the same practitioner.
session_streaming
End was called while a streaming worker is still attached.
invalid_session_state
The session is not in a state that permits the requested transition.
conflict
Create used an external_id that another provider's session already holds.
Zoom
Zoom Session Lifecycle
The Scribe API manages the full Zoom capture lifecycle server-side. The browser provides a meeting link and disclosure preferences; it never handles Zoom access tokens or communicates with internal capture services.
Create a Zoom Session
Supply a canonical visit_type that resolves to a supported note template, and the provider's IANA timezone. If note_template is supplied, it must match that visit type. Missing or unsupported selections fail before a session is created, even though visit_type is optional in the generated request model. Template variants and local_timezone follow the rules in Create an In-Person Session.
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.
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).
Successful Response
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.
meeting_link is not a valid Zoom meeting URL (invalid_meeting_link).
Bearer token is absent or invalid.
The principal is outside this workspace or provider scope.
The practitioner already has an active Zoom session (active_zoom_session_exists), the idempotency key was reused with a different fingerprint (idempotency_key_conflict), the provider has not connected Zoom (zoom_not_connected), Zoom rejected the stored credential and the provider must reconnect (zoom_reconnect_required), or the Zoom meeting hasn't started yet — start the meeting, then retry (zoom_meeting_not_started).
Request validation failed (validation_error); no canonical visit_type was supplied to resolve a supported amd-* template (note_template_required); or the visit_type/note_template does not map to a supported amd-* template (unsupported_note_template).
Bot dispatch failed; the session was compensated/cancelled (bot_dispatch_failed), or refreshing the stored Zoom token was temporarily unavailable (zoom_refresh_unavailable).
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"
}Creates a Zoom capture session in one call. The server validates the meeting link, confirms the provider has a connected Zoom account, creates the session, and dispatches the capture bot.
Error responses:
422
validation_error
The meeting link is not a Zoom URL, or another field failed validation.
422
note_template_required, unsupported_note_template
Visit type and template rules from Create an In-Person Session.
409
active_zoom_session_exists
The provider already has an active Zoom session.
409
idempotency_key_conflict
The external_id was reused with different parameters.
409
zoom_not_connected
The provider has not connected Zoom.
409
zoom_reconnect_required
Zoom rejected the stored connection; the provider must reconnect Zoom.
409
zoom_meeting_not_started
The meeting has not started. The session was cancelled; start the meeting, then create a new session.
503
zoom_refresh_unavailable
Refreshing the Zoom connection is temporarily unavailable. Retry.
503
bot_dispatch_failed
Bot dispatch failed; the session was automatically cancelled. Retryable.
Pause Zoom Bot
Pause the Zoom capture bot bound to an owned session (proxies the control plane).
Successful Response
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.
Bearer token is absent or invalid.
The principal is outside this workspace or provider scope.
No provider-owned resource exists at this URL.
No live Zoom bot is bound to this session (bot_not_active).
Validation Error
The control plane failed to deliver the command (bot_command_failed).
POST /v1/{workspace_id}/sessions/{session_id}/zoom/pause HTTP/1.1
Accept: */*
{
"bot_status": "text"
}Pauses the capture bot bound to the session.
Error responses:
409
bot_not_active
No live bot is bound to this session.
502
bot_command_failed
The command could not be delivered.
Resume Zoom Bot
Resume the Zoom capture bot bound to an owned session (proxies the control plane).
Successful Response
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.
Bearer token is absent or invalid.
The principal is outside this workspace or provider scope.
No provider-owned resource exists at this URL.
No live Zoom bot is bound to this session (bot_not_active).
Validation Error
The control plane failed to deliver the command (bot_command_failed).
POST /v1/{workspace_id}/sessions/{session_id}/zoom/resume HTTP/1.1
Accept: */*
{
"bot_status": "text"
}Resumes a paused capture bot. Same response and error contract as pause.
End Zoom Session
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.
Successful Response
Response of DELETE /sessions/{id}/zoom: acknowledges that the drain + internal zoom-finalize
is underway. The session flips to in-review asynchronously (reaper backstop).
Bearer token is absent or invalid.
The principal is outside this workspace or provider scope.
No provider-owned resource exists at this URL.
The session is not a Zoom session (not_zoom_session).
Validation Error
DELETE /v1/{workspace_id}/sessions/{session_id}/zoom HTTP/1.1
Accept: */*
{
"status": "text"
}Ends a Zoom session. Returns immediately; the platform stops the bot and finalizes the transcript in the background. Inspect the session and artifact state for the eventual result; the 202 acknowledgement does not establish that finalization has completed.
Idempotent - a second delete returns 202 and is a no-op.
Error responses:
409
not_zoom_session
The session is not a Zoom session.
Zoom Connection
Providers can connect their Zoom account through the Scribe API. The platform manages the OAuth lifecycle server-side - the browser initiates the flow and reads connection status, but never handles token material.
Endpoints
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.
""Successful Response
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.
Bearer token is absent or invalid.
The principal is outside this workspace or provider scope.
Validation Error
Zoom connect is temporarily unavailable; retry.
POST /v1/{workspace_id}/zoom/oauth/authorize HTTP/1.1
Accept: */*
{
"authorize_url": "text",
"expires_at": "2026-01-01T00:00:00.000Z"
}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.
""""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
Successful Response
Validation Error
GET /v1/{workspace_id}/zoom/oauth/callback HTTP/1.1
Accept: */*
No content
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).
Successful Response
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.
falseBearer token is absent or invalid.
The principal is outside this workspace or provider scope.
Validation Error
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
}Disconnect Zoom: best-effort revoke at Zoom, delete the row. Idempotent -- deleting a non-existent connection still returns 204.
Successful Response
No content
Bearer token is absent or invalid.
The principal is outside this workspace or provider scope.
Validation Error
DELETE /v1/{workspace_id}/zoom/connection HTTP/1.1
Accept: */*
No content
The callback is the authorization redirect target and should not be called directly.
Authorize Response
Navigate to the authorization URL returned by the Zoom authorize operation while it remains valid. The browser follows the consent flow; it does not handle provider token material.
Connection Response
Read the Zoom connection operation to confirm the provider connection before creating a capture session. A connected account does not establish that a particular meeting is ready to capture.
Error Handling
401
Missing or invalid bearer token.
403
Insufficient scope or provider mismatch.
503
Zoom connect is temporarily unavailable (the authorize endpoint could not mint a verifiable state). Retry after a brief delay.
The callback endpoint never returns JSON. Every outcome - success or failure - results in a 302 redirect to the configured web settings page with a zoom_connected=true or zoom_error=<reason> query parameter.
Last updated
Was this helpful?

