Skip to content

Typed client (docgen_client)

The product-owned, authoritative async client for DocGen's /v1 REST API — what consumers (client-site, client-rwdi, client-odyssey, client-tomorrowlaw) import instead of hand-rolling an HTTP client against the /v1 contract. It is a standalone distribution (docgen-client, top-level module docgen_client) depending only on httpx + pydantic; importing it never pulls in the DocGen server's dependencies (altaforge, sqlalchemy, fastapi).

The reference below is generated directly from the client's source docstrings by mkdocstrings — so it cannot drift from the code. Only the public client package is documented here; DocGen's internal server modules are deliberately not part of the public reference.

docgen_client

The authoritative typed client for DocGen's /v1 REST API.

from docgen_client import DocGenClient

Consumers (client-site, client-rwdi, client-odyssey, client-tomorrowlaw) import this instead of hand-rolling their own HTTP client against DocGen's /v1 contract. This is a standalone distribution (docgen-client, top-level module docgen_client) depending on only httpx + pydantic — see _client.py's module docstring for the auth wiring, the per-call tenant scope, the SSE deadline fix, and the import-safety guarantee, and this package's README.md for why it is a separate distribution rather than docgen.client. See docs/superpowers/specs/2026-07-25-shared-docgen-client-design.md in altaml/client-site for the cross-repo migration plan.

DocGenClient

DocGenClient(base_url, token_provider, default_org_slug=None, *, transport=None, poll_timeout_s=_DEFAULT_POLL_TIMEOUT_S, poll_interval_s=_DEFAULT_RECONNECT_BACKOFF_S)

Async HTTP client for DocGen's /v1 REST contract.

Configure endpoint + token source, and an optional default tenant.

Parameters:

Name Type Description Default
base_url str

DocGen's base URL. Empty string = unavailable (see available); every method then raises DocGenUnavailableError.

required
token_provider TokenProviderLike

a bearer-token source, called per request.

required
default_org_slug str | None

the tenant sent as X-Org-Slug when a call doesn't pass its own org_slug override. Immutable for the life of the client — never mutate this field between requests; a caller serving multiple tenants from one client instance must pass org_slug explicitly on every call instead.

None
transport AsyncBaseTransport | None

injected in tests (httpx.MockTransport); None in production so httpx uses its default network transport.

None
poll_timeout_s float

default budget for watch_progress when the caller doesn't pass an explicit timeout_s.

_DEFAULT_POLL_TIMEOUT_S
poll_interval_s float

backoff between the one reconnect watch_progress allows when a stream ends without a terminal event (a dropped connection, not a timeout — a genuine deadline miss is never retried, see _sse.read_progress_stream).

_DEFAULT_RECONNECT_BACKOFF_S

available property

available

True when a DocGen endpoint is configured.

aclose async

aclose()

Release the underlying connection pool.

__aenter__ async

__aenter__()

Async-context-manager support: async with DocGenClient(...) as client:.

__aexit__ async

__aexit__(*exc_info)

Release the connection pool on context exit, success or failure.

list_templates async

list_templates(*, org_slug=None, bearer_token=None)

Return every template document in the org (GET /v1/templates).

Returns the raw templates list — open shape, same rationale as get_document/check_readiness (see models.py's module docstring).

Parameters:

Name Type Description Default
bearer_token BearerTokenLike | None

overrides the constructor's token provider for this call — a str verbatim, or a Callable[[], str] invoked fresh per HTTP request. See _headers.

None

get_template async

get_template(slug, *, org_slug=None, bearer_token=None)

Resolve one template by its stable, org-unique slug (GET /v1/templates/{slug}, #346) — preferred over a title match, which breaks on rename.

Returns the raw document dict. Org-scoped: a slug that exists only in another tenant is a 404, surfaced here as DocGenError.

Parameters:

Name Type Description Default
bearer_token BearerTokenLike | None

overrides the constructor's token provider for this call — a str verbatim, or a Callable[[], str] invoked fresh per HTTP request. See _headers.

None

create_template_from_spec async

create_template_from_spec(spec, *, dry_run=False, org_slug=None, bearer_token=None)

Compile a declarative template spec into the org (POST /v1/templates).

The spec is the JSON body — the documented headless authoring surface, so a consumer bootstraps its house template with no product-side seed. Idempotent: existing keys skip-with-warning. dry_run=True validates the spec without persisting. Returns the compile result + template.

Parameters:

Name Type Description Default
bearer_token BearerTokenLike | None

overrides the constructor's token provider for this call — a str verbatim, or a Callable[[], str] invoked fresh per HTTP request. See _headers.

None

update_template_spec async

update_template_spec(document_id, spec, *, org_slug=None, bearer_token=None)

Apply a spec to update an existing template in place (PUT /v1/documents/{id}/spec).

The update-in-place counterpart to create_template_from_spec, which is create-only (existing keys skip-with-warning) and so cannot converge a template that already exists. Sections/fields/data-needs/ config and the org entities the spec redefines are synced to match the spec — the read-modify-write loop is GET …/spec -> edit -> update_template_spec. document_id is the template's document id (resolve it via get_template/list_templates first — this call takes the id, not the slug). Returns the applied document.

Parameters:

Name Type Description Default
bearer_token BearerTokenLike | None

overrides the constructor's token provider for this call — a str verbatim, or a Callable[[], str] invoked fresh per HTTP request. See _headers.

None

list_documents async

list_documents(*, org_slug=None, bearer_token=None)

List the caller-org's document instances (GET /v1/documents).

Returns the raw documents list — open shape, same rationale as list_templates (see models.py's module docstring).

Parameters:

Name Type Description Default
bearer_token BearerTokenLike | None

overrides the constructor's token provider for this call — a str verbatim, or a Callable[[], str] invoked fresh per HTTP request. See _headers.

None

list_texts async

list_texts(*, org_slug=None, bearer_token=None)

List the org-configured Texts / phrase atoms (GET /v1/texts).

list_snippets async

list_snippets(*, org_slug=None, bearer_token=None)

List the org-configured Snippets / clause molecules (GET /v1/snippets).

list_risk_blocks async

list_risk_blocks(*, org_slug=None, bearer_token=None)

List the org-configured Risk Blocks (GET /v1/risk-blocks).

Unwraps the riskBlocks key (DocGen returns this one camelCase).

list_orchestration_rules async

list_orchestration_rules(*, org_slug=None, bearer_token=None)

The orchestration rulebook — what binds which molecule (GET /v1/orchestration-rules).

create_document async

create_document(template_id, title, fields=None, *, org_slug=None, bearer_token=None)

Instantiate a template into a new document (POST /v1/documents).

Returns the raw response body ({'document': {...}}) — DocGen documents itself as an open shape (see models.py's module docstring), so this is a pass-through, not a typed model.

Parameters:

Name Type Description Default
org_slug str | None

tenant override for this call; defaults to the client's default_org_slug when omitted.

None
bearer_token BearerTokenLike | None

overrides the constructor's token provider for this call — a str verbatim, or a Callable[[], str] invoked fresh per HTTP request. See _headers.

None

orchestrate async

orchestrate(document_id, source_text, *, org_slug=None, bearer_token=None)

Run the registered orchestrator over source_text (POST /v1/documents/{id}/orchestrate).

Returns the review payload (plan, bound molecules, entity names, set field values) as a raw dict.

generate async

generate(document_id, variables=None, *, force=False, preserve_edited=False, org_slug=None, bearer_token=None)

Fire the render pipeline (POST /v1/documents/{id}/generate).

Fire-and-return — completion arrives via watch_progress. A document with hand-edited sections and neither force nor preserve_edited set is refused with a 409, surfaced here as a terminal DocGenError (never retried blindly — the caller must re-issue with an explicit choice, per DocGen's confirm-before-overwrite gate).

watch_progress async

watch_progress(document_id, *, timeout_s=None, reconnect_backoff_s=None, org_slug=None, bearer_token=None)

Watch GET /v1/documents/{id}/progress (SSE) until a terminal event, or timeout_s elapses.

Terminal events: pipeline_finished, pipeline_failed, pipeline_blocked — a hard readiness block emits pipeline_blocked and the pipeline returns without ever emitting finished/failed, so waiting past it is a guaranteed timeout.

The SSE stream can end without a terminal event (idle timeout, a transient network drop) with no error — reconnect once after reconnect_backoff_s before giving up. A genuine deadline miss (including a silent stream that never emits at all — the bug this client's asyncio.timeout_at wrapper exists to fix) is DocGenTransientError and is never retried within this call.

Parameters:

Name Type Description Default
timeout_s float | None

total wall-clock budget for the ENTIRE call — the initial connection AND the one reconnect share it. Computed once into a single absolute budget (asyncio.get_running_loop().time() + timeout_s) before the first attempt, so the reconnect never resets the caller's timeout. Defaults to poll_timeout_s (the constructor default).

None

get_document async

get_document(document_id, *, org_slug=None, bearer_token=None)

One document: header + sections (GET /v1/documents/{id}).

Returns {'document': {...}, 'sections': [...]} as a raw dict.

edit_section_output async

edit_section_output(document_id, section_id, content, *, org_slug=None, bearer_token=None)

Push a hand-edited section output to DocGen (PUT /v1/documents/{id}/sections/{section_id}/output).

Marks the section edited on DocGen's side and records a new section-output version, so a subsequent force-less generate never silently overwrites it.

check_readiness async

check_readiness(document_id, *, org_slug=None, bearer_token=None)

Run DocGen's pre-generation readiness gate without generating (GET /v1/documents/{id}/readiness).

Returns {generation_should_proceed, ui_state, gaps}. Informational only; a genuinely blocking failure still surfaces via the render pipeline's own pipeline_blocked event.

export_docx async

export_docx(document_id, *, org_slug=None, bearer_token=None)

Export a rendered document as .docx (GET /v1/documents/{id}/export.docx).

The route returns the binary .docx directly (not base64 JSON), with the filename in Content-Disposition. Returns (filename, content).

TokenProvider

Bases: Protocol

Structural match for altaforge.auth.client.ServiceTokenProvider.

Only .token() is used; the provider owns caching and refresh-before-expiry internally, so it is called once per request. A bare Callable[[], str] also satisfies TokenProviderLike (see below) — the client dispatches on which shape it was given.

token

token()

Return a currently-valid Bearer token string.

DocGenError

Bases: RuntimeError

Raised when DocGen returns an unexpected response.

Terminal by default: a caller should not suggest retrying without other evidence, since most of these come from DocGen's own pipeline_failed / pipeline_blocked events, which carry only a message string (no exception type) — see DocGenTransientError for the cases we CAN tell are worth a retry.

DocGenTransientError

Bases: DocGenError

A DocGen failure a retry might resolve.

Narrowly scoped — deliberately NOT every 5xx:

  • HTTP 429/502/503/504 (typically transient upstream load).
  • An httpx.TransportError (connection refused/reset, DNS failure, or a connect/read/write/pool timeout) — the request never got a response at all, so retrying with the same inputs is safe to attempt.

A bare HTTP 500 stays a terminal DocGenError: DocGen maps an unhandled server exception (a permanent bug or config error, e.g. a missing setting) to a plain 500 with no distinguishing status code — treating it as transient would tell a caller to retry a request that will never succeed. This is not a hypothetical: SITE hit exactly this incident when an earlier classification treated all 5xx as transient.

DocGenUnavailableError

Bases: DocGenError

Raised when no DocGen endpoint is configured (empty base_url).

CreateDocumentRequest

Bases: BaseModel

Mirrors api_v1.CreateDocumentBody field-for-field — POST /v1/documents.

Deliberately no snake_case alias: this model is write-only (built and immediately serialized inside DocGenClient.create_document, never parsed from JSON), so there is no ergonomic benefit to a Python-side snake_case name — and a bare field name here, rather than Field(alias=...), sidesteps a real mypy wrinkle: pydantic's BaseModel uses PEP 681 dataclass_transform, which mypy honors by synthesizing the constructor from each field's alias regardless of a runtime-only populate_by_name setting. Aliasing bought nothing here and cost a snake_case construction call site that wouldn't type-check under mypy --strict.

GenerateRequest

Bases: BaseModel

Mirrors api_v1.GenerateBody field-for-field — POST /v1/documents/{id}/generate.

Bare preserveEdited field name (no snake_case alias) for the same reason as CreateDocumentRequest — write-only, and aliasing fights mypy --strict's dataclass_transform-based constructor synthesis for no ergonomic gain.

GenerateResponse

Bases: BaseModel

Mirrors api_v1.GenerateResponse — the generate 200 body.

OrchestrateRequest

Bases: BaseModel

Mirrors api_v1.OrchestrateBodyPOST /v1/documents/{id}/orchestrate.

Already snake_case on the wire (unlike the top-level camelCase DTOs) — matches api_v1.py's own field name exactly, no alias needed.

ProgressEvent

Bases: BaseModel

One terminal SSE event from GET /v1/documents/{id}/progress.

event is the only field every terminal event is guaranteed to carry (pipeline_finished / pipeline_failed / pipeline_blockeddocgen.progress.TERMINAL_EVENTS); the rest of the payload varies by event type (e.g. pipeline_failed carries an error message, pipeline_blocked carries gap detail), so extra keys are allowed through rather than enumerated — DocGen's own progress payload is not a fixed schema either (see docgen.progress.emit). seq, when present, lands in model_extra like any other extra key; _sse.read_progress_stream reads it from the raw event dict (not this model) to dedup replayed events across a reconnect — see that module's docstring.

SectionOutputRequest

Bases: BaseModel

Mirrors api_v1.SectionOutputBodyPUT …/sections/{id}/output.