POST /documents.
Built for healthcare AI workflows where output shape, provenance and reproducibility matter as much as the generated content itself.
Guided Documents generation is interaction-optional: supply explicit context (text / transcript / facts) for stateless calls, or supply an interactionId to pull facts and transcripts already attached to an interaction. See the full API specification on POST /documents.
A single endpoint with four template-supply paths — from a plain
templateRef for production traffic to a fully inline dynamicTemplate for prototyping. Every path returns a typed document plus the resolved templateId / templateVersionId so generations are auditable and drift-proof.The
POST /documents endpoint is available in beta. Feedback welcome at help@corti.ai or via your Corti contact.Features
Flexible request shape
One endpoint, four template-supply paths — reference a stored template, patch it with overrides, assemble from stored sections, or define everything inline. Pick the right shape per call.
Combine input context
Pass any combination of
text, transcript or facts items explicitly.Auto-pull context
Let the API do the work for you and simply reference an
interactionId to pull and pass attached facts and transcripts implicitly to the model.Output schema control
Steer generation into typed shapes and structured data outputs —
string, number, boolean, array, object — combined with rendering primitives like itemFormat and fieldFormat for advanced string outputs.Drift-proof aggregates
Paths 2–4 persist a snapshot of the resolved template + sections used. The returned
templateId reproduces the exact generation.The four template-supply paths
The endpoint is designed for the following needs:- Repeatable, governed generation — reference a stored, versioned template by UUID.
- Light-touch overrides — keep your base template, but patch a section’s title, instructions or output schema for a single call.
- Compose at runtime — pick stored sections out of your library and assemble them on the fly.
- Try something new — define everything inline without saving anything first.
templateId and templateVersionId that were used.
You pick one path per call. The four behave differently with respect to whether a new template aggregate is persisted as a side-effect of the call.
Each path has its own page so you can land directly on the one that fits your scenario.
Path 1 — Plain templateRef
Reference a stored template by UUID. No overrides, no side-effects. The lightest path for production traffic.
Path 2 — templateRef + overrides
Keep your base template, patch instructions / heading / schema for a single call. Auto-generates a drift-proof aggregate.
Path 3 — assemblyTemplate
Build a template on the fly from existing stored sections. Useful for EHR-field-driven note composition.
Path 4 — dynamicTemplate
Define the template and every section inline. Ideal for prototyping and one-off generations.
When to use which path
- Production ambient flow
- Per-user customization
- EHR-field-driven note
- Prototype / experimental
Use Path 1 (plain
templateRef) with a pinned templateVersionId for reproducibility. No side-effects, fastest path, easiest to audit.Drift-proof snapshots. For every path except plain
templateRef, the auto-generated template aggregate snapshots the fully resolved content at request time. Subsequent edits to base templates or sections do not affect previously generated documents. You can also reuse that auto-generated template in future calls by reading its ID from the response.Input context
Every call needs to tell the model what to reason over. Supply exactly one of:context— an ordered array of explicit context items. Each item is one of three discriminated types (see below).interactionId— when supplied, all non-discarded facts and transcripts already attached to the referenced interaction are passed implicitly as input context.
context and interactionId are currently mutually exclusive — supply one or the other, never both. Combining them to extend the implicit interaction context with extra explicit items is on the roadmap; see the release notes for status.Context item types
type | Required field | Use when |
|---|---|---|
text | text (string) | Pasting a referral letter, dictation, EHR field, or any free-form snippet |
transcript | transcript (minimal shape — transcripts[] of segments, optional metadata.participantsRoles) | Feeding a conversation transcript directly without going through /streams |
facts | fact.text (string, required); fact.group (string, optional) | Passing a single fact derived elsewhere (one fact per item — repeat the item to pass many). group is optional metadata you can carry over (e.g. "history-of-present-illness", "allergies") when migrating from Classic facts or grouping facts by category. |
Mixed context array
Common request shape
All four paths share three top-level fields and differ only in which template-supply field they carry:Shared shape
outputLanguage is required and uses a BCP-47 tag.
Response shape
All four paths return the same response shape: adocument object plus a usageInfo block. When the request uses the default retention policy, the document is also persisted with its own id, createdAt and updatedAt. With X-Corti-Retention-Policy: none, the response is the ephemeral variant — the same document body without the persisted-resource fields.
Response (default retention)
The keys in
stringDocument (and structuredDocument) are section UUIDs, not slugs or section keys. The slug-looking placeholders above stand in for the actual UUIDs. See Mapping response section IDs back to your sections below for how to correlate them to the sections in your request.document.id— the persisted document UUID. Only present on the default-retention response; omitted when the request setsX-Corti-Retention-Policy: none.document.templateId/document.templateVersionId— the template (and version) that was actually used. For Path 1 this is the referenced template; for Paths 2–4 it is the newly persisted auto-generated aggregate. Store these if you want full traceability of what produced each document.document.stringDocument— an object keyed by section ID containing the rendered string output for each section. Always present.document.structuredDocument— present when one or more sections use a non-stringoutputSchema(object, array, etc.). Keyed by section ID; each entry holds the structured object/array as the schema declared.usageInfo.creditsConsumed— credits consumed for this request. Same shape as every other Corti endpoint that returns usage information.
Mapping response section IDs back to your sections
BothstringDocument and structuredDocument are keyed by section ID. For Path 1 (plain templateRef) and Path 2 (templateRef + overrides) those IDs match the sections of the referenced template — you can map them straight back. For Path 3 (assemblyTemplate) and Path 4 (dynamicTemplate) the keys are IDs Corti generates for the auto-saved aggregate. Path 3 carries explicit sectionRefs[].sectionId you can correlate by, but Path 4 is the gotcha: your request supplies an ordered list of SectionGeneration objects with no IDs of your own, so the response keys arrive as opaque server-side IDs with no obvious mapping back to your inline section definitions — meaning you can’t reliably attach the right heading to each rendered section just from the response.
Workaround until this is improved. Use the returned templateVersionId to read the saved aggregate:
sections[] array preserves your request order and carries the resolved section IDs. Build a position → sectionId → heading mapping from that response, then key the rendered output against it.
This is a known DX gap for Path 4 specifically. The roadmap includes returning client-supplied identifiers (or section ordering) directly on the response so the follow-up GET isn’t needed.
Auto-generated template aggregates — 30-day retention
Paths 2, 3 and 4 persist an auto-generated template aggregate as a side-effect of generation — a drift-proof snapshot of the resolved template + sections that were actually used. Path 1 (plaintemplateRef) is the exception: it uses the referenced template as-is and persists nothing new.
Errors and validation
| Status | When it happens |
|---|---|
400 Bad Request | Request body fails basic validation (e.g. missing outputLanguage, malformed JSON). |
404 Not Found | A referenced templateId, templateVersionId or sectionId does not exist. |
422 Unprocessable Entity | Request is structurally valid but semantically rejected — e.g. none of templateRef/assemblyTemplate/dynamicTemplate supplied, the referenced template has no published version, an override targets a section not linked to the base template version, or you supplied more than one of the three. |
502 Bad Gateway | Document generation failed downstream. |
Provide exactly one of
templateRef, assemblyTemplate, dynamicTemplate. Supplying zero or more than one yields a 422.Failure modes & generation behavior
Behaviors and edge cases worth knowing about before you build. Expand the topic you need.All-or-nothing on generation failure
All-or-nothing on generation failure
If any step in the generation pipeline fails — even after internal retries — the entire request fails with an appropriate error response. You will not receive a partial document with some sections filled and others silently missing.This is deliberate: partial documents are more dangerous in clinical workflows than an explicit failure. If your integration needs resilience for individual sections, generate them in separate requests so a single failure doesn’t take down the whole document.
Duplicate sections in the same request
Duplicate sections in the same request
The API validates against using the same
sectionId in the same POST /documents request and fail the request.Empty sections — when the source has nothing relevant
Empty sections — when the source has nothing relevant
How a section renders when the source material doesn’t support meaningful content depends on what you defined:
For sections that always need to render a clear placeholder so downstream consumers can rely on consistent structure, set
| Defined on the section | Behavior when nothing relevant in source |
|---|---|
default on the outputSchema | The section renders the default (e.g. "Not discussed", "Nil", 0, false). Deterministic — downstream consumers can rely on it. |
Fallback phrasing in instructions (e.g. contentPrompt says “output ‘Not assessed’ if the source doesn’t mention this domain”) | The model emits the prompted fallback. Reliable in practice but ultimately steered by the LLM. |
| Neither set | The LLM decides. It will often emit a brief “not discussed” phrasing; if it truly has nothing to say, the section returns an empty string. |
default on the schema or encode the fallback in instructions. Don’t rely on the LLM picking the right empty-state phrasing on its own.How guided synthesis relates to other endpoints
- It is separate from
POST /interactions/{id}/documents(Documents Classic). The classic endpoint continues to work as deprecated if you’re still usingtemplateKey-based generation. See Migrate from Classic. - It consumes the new resources authored via the Sections and Templates APIs.
- The
outputSchemareference is shared with section authoring — see the section-creation guide.
Next steps
Create a Section
Author and version the sections you’ll reference in Paths 1–3.
Create a Template
Compose sections into versioned templates ready for Path 1.
Corti Standards
Reference the Corti-curated section and template library from any of the four paths.
Migrate from Classic
Field-by-field mapping from
POST /interactions/{id}/documents to POST /documents.