Authoring & Configuring DocGen — the /v1 API (for humans and agents)¶
DocGen runs as a service. Everything about a document or template — its
sections, prompts, models, data sources, voice, composition — is created and
configured over the versioned REST API at /v1. There is no back door: no
editing spec files on disk, no writing the database directly. If you can't do it
through the API, it isn't a supported operation.
This guide is written for two readers:
- Humans building a UX or operating DocGen — the concepts + the workflow.
- Agents (e.g. Claude) driving DocGen headlessly — the exact endpoints, the spec shape, and the discipline to follow.
Two ways to author — pick either, they're interchangeable¶
Every authoring task can be done either way:
- Granular API in code — call individual endpoints: instantiate, read/patch config, edit a section, set a field.
- Author a spec and apply it — write a whole spec object (the same shape
as the bundled demo specs) and apply it: create a new template
(
POST /templates) or update an existing one in place (PUT …/spec). Read it back withGET …/spec, edit, re-apply.
The spec is just the whole-object form of the same configuration. Use spec apply for structural work (adding/removing/reordering sections, editing composition); use the granular config API for targeted tweaks.
Core concepts¶
Template vs. instance. A template is a reusable document pattern
(is_template = true). You instantiate it into a concrete document, fill its
fields, and generate. Both are the same Document shape, so the config API works
on either.
The data tiers (a load-bearing rule).
| Tier | What | Configured via |
|---|---|---|
Facts — fields / gathered / computed |
The single source of truth (numbers, dates, deal values). Never round-trip through an LLM. | fields in config/spec; data_needs (gather) + compute hooks |
Framing — synth.brief |
LLM judgment/positioning only, post-compute. Emits no number/date. | brief in config/spec |
Boilerplate — texts / snippets / risk_blocks |
Deterministic reusable content + composition. | spec texts/snippets/risk_blocks |
Sections & render modes. Each section renders in one mode:
synthesized (LLM, written in the batched mega-call), templated
(deterministic Jinja), composed (assembly of snippets), isolated
(per-section LLM). synthesized/isolated need a prompt;
templated/composed need an output_template.
Models per step. Four phases, each independently configurable:
gather (retrieval), synthesize, render, verify. Unset falls back to
model then the env default. Set them in the models group (config) or the
template block (spec).
synth_group (batching). The synthesize mega-call is split into batches of
≤ 8 sections so a dropped field can't fail the whole document. Sections sharing a
synth_group are written together (coordination); it only matters above the cap.
Set it per section.
/v1 endpoint reference¶
Auth: every request carries the bearer token and X-Org-Slug header;
DocGen scopes all data to the caller's org (an out-of-scope org → 404).
Authoring & config¶
| Method + path | Purpose |
|---|---|
GET /v1/config-schema |
Self-describing surface — the spec's JSON schema + the config group keys. Start here to discover what's configurable. |
POST /v1/templates |
Create a template by compiling a provided spec. ?dry_run=true validates + reports without writing. |
GET /v1/documents/{id}/spec |
Export a template/document to canonical spec JSON (read-modify-write). |
PUT /v1/documents/{id}/spec |
Update in place from a full spec (the spec is the desired state; syncs sections/fields/data-needs/config + redefined composition). |
GET /v1/documents/{id}/config |
Read the config object (models-per-step, synthesis, brief, voice, verify, render, tools, fields, sections). |
PUT /v1/documents/{id}/config |
Atomic partial config patch — only the groups/keys you send change. |
PUT /v1/documents/{id}/sections/{sid} |
Edit one section (title, render_mode, prompt, output_template, output_type, synth_group). |
PUT /v1/documents/{id}/fields |
Set/patch field values. |
GET /v1/templates |
List templates the caller can instantiate. |
GET /v1/texts · /snippets · /risk-blocks · /orchestration-rules |
The org's building blocks. |
Instantiate, generate, read¶
| Method + path | Purpose |
|---|---|
POST /v1/documents |
Instantiate a template into a new document (optionally seed field values). |
POST /v1/documents/{id}/orchestrate |
Run the orchestration rulebook (bind molecules). |
POST /v1/documents/{id}/gather |
Run gather; return the source data (no LLM). |
POST /v1/documents/{id}/generate |
Start async generation (progress via SSE). |
GET /v1/documents/{id}/progress |
SSE feed of generate progress. |
GET /v1/documents/{id}/readiness |
Readiness verdict + gaps. |
GET /v1/documents/{id} |
One document: header + sections (+ sources). |
GET /v1/documents |
List instances (+ field values). |
GET /v1/documents/{id}/export.docx |
Download the composed .docx. |
PUT /v1/documents/{id}/state |
Set lifecycle state. |
| snapshots / section-versions | Versioning of doc state + section output. |
The spec shape¶
GET /v1/config-schema returns the authoritative JSON schema. In brief, a spec
is:
{
"name": "my-template",
"description": "...",
"template": {
"title": "My Template",
"is_template": true,
"verify_cited_sources": false,
"model": null, "gather_model": null, "render_model": null, "verify_model": null,
"synthesis_instruction": null,
"synthesis_prompt_template": null, // coordination wrapper for the mega-call
"synthesis_grounding": "strict",
"system_prompt": null,
"synthesis_brief_prompt": null, // the framing brief (no facts)
"synthesis_brief_fields": null,
"voice": null, // a voice name declared in voices[]
"snippet_bindings": []
},
"fields": [{ "key": "client", "value_type": "string", "required": true, "label": "Client" }],
"sections": [{ "title": "Overview", "render_mode": "synthesized", "prompt": "...", "group": "intro" }],
"data_needs": [{ "name": "research", "kind": "prompt", "tool_ref": "claude_web_search",
"prompt_body": "...", "output_key": "research", "depends_on": [] }],
"texts": [], "snippets": [], "risk_blocks": [], "voices": [], "tools": [], "instances": []
}
Ordering rule: a snippet_binding must reference a snippet declared in
snippets[] (or already in the org) — declare before bind.
Worked flows¶
A. Author a new template (spec apply)¶
1. GET /v1/config-schema # discover the shape
2. POST /v1/templates?dry_run=true {spec} # validate — writes nothing
3. POST /v1/templates {spec} # create it
4. POST /v1/documents {templateId, title, fields}# instantiate
5. POST /v1/documents/{id}/generate # generate (watch /progress)
6. GET /v1/documents/{id}/export.docx # download
B. Tweak an existing template (granular)¶
GET /v1/documents/{id}/config
PUT /v1/documents/{id}/config { "models": {"synthesize": "claude-sonnet-4-6"},
"sections": [{"id": "...", "prompt": "..."}] }
id and preserve the section.
C. Restructure a template (read-modify-write via spec)¶
spec = GET /v1/documents/{id}/spec
# add/remove/reorder sections, edit data_needs, change composition in `spec`
PUT /v1/documents/{id}/spec {spec} # applied in place; spec is the desired state
PUT /spec for structure; use POST /templates (new title) to clone as a
new version instead of editing in place.
Guidance for agents (how you should drive this)¶
- Discover first. Call
GET /v1/config-schemabefore authoring a spec you haven't seen the shape of. Don't guess field names. - Validate before writing. Use
POST /templates?dry_run=true(or generate a spec, dry-run it) so a bad spec fails without side effects. - Pick the right path. Structural change (add/remove/reorder sections,
composition) → author/edit the whole spec and
PUT /spec. A one- or two-field tweak →PUT /config. - Never fabricate facts. Numbers/dates/amounts belong in
fields/ gathered data / compute — never write them into a prompt or brief and never let the synthesis LLM originate them. - No back doors. Do not edit files under
authoring/specs/, do not touch the database, do not reseed to "apply" a change. Everything goes through/v1. - Idempotence & ordering.
POST /templatesskips existing keys (create-only); to update, usePUT /spec. Declare snippets before binding them.
For humans building a UX¶
- Treat
PUT /specas save the whole document andPUT /configas save a field — a UX can do optimistic local edits, then persist either way. PUT /specreplaces the doc's section graph, so section ids change on a structural save — re-read after saving; don't cache ids across aPUT /spec.GET /config-schemapowers form generation + validation hints.- Use
?dry_run=truefor a "Validate" button; surfacecompile.warnings.
Design & rationale: docgen/docs/design/specs/2026-07-11-headless-config-api-design.md.
Pipeline internals (gather → compute → synthesize → render → verify):
docs/releases/r1-framework-v0-core/r1-tdd.md.