Skip to content

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:

  1. Granular API in code — call individual endpoints: instantiate, read/patch config, edit a section, set a field.
  2. 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 with GET …/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
Factsfields / 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
Framingsynth.brief LLM judgment/positioning only, post-compute. Emits no number/date. brief in config/spec
Boilerplatetexts / 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": "..."}] }
Only the keys you send change; section edits are by 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
Use 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-schema before 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 /templates skips existing keys (create-only); to update, use PUT /spec. Declare snippets before binding them.

For humans building a UX

  • Treat PUT /spec as save the whole document and PUT /config as save a field — a UX can do optimistic local edits, then persist either way.
  • PUT /spec replaces the doc's section graph, so section ids change on a structural save — re-read after saving; don't cache ids across a PUT /spec.
  • GET /config-schema powers form generation + validation hints.
  • Use ?dry_run=true for a "Validate" button; surface compile.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.