Field Sheet Proposals
Start model proposals for where each template field gets its value, poll the run, confirm the suggested sources, and rescan a Word file for new fields.
A proposal is a model run that suggests a source and a reason for every unconfirmed field of a template. It reads the template text, the fields in context, and the columns of the chosen data product (with the columns of the reference table its Spec binds), and stores each suggestion as unconfirmed. The run continues after the request returns, so the start routes answer 201 with the running proposal and the state is polled with GET /v1/doc-gen/templates/{id}/proposal until status is done or failed.
A proposal never invents a source. Only columns the product or the bound reference table has, rules the workspace has, and patterns that match a sampled value survive validation; everything else is left as "entered per document" with the model's reason kept for the person. A field a person confirmed is not changed. A request for a run already in progress for the same product and mode returns that run, and a second start at the same moment can answer 409, which is retryable. A field that got no answer is listed in summary.pending and asked for again by the next proposal for the same product.
POST /v1/doc-gen/templates/propose starts proposals for several templates at once, up to limit (default and most 10, since each proposal is a model run). By default it chooses only templates not yet proposed for the product; only_unproposed: false and force widen that. With dry_run: true nothing starts and the response names the templates that would. Confirmation is a separate, deliberate step: POST /v1/doc-gen/templates/{id}/confirm marks suggested sources as confirmed, either named fields or every unconfirmed one.
/v1/doc-gen/templates/{id}/proposeBody fields
/v1/doc-gen/templates/{id}/proposalProposal fields
Response: Proposal done with one field pending
{
"proposal": {
"status": "done",
"runId": "a8d3c1f0-5e7b-4f21-bd34-0c9e6a1f7b52",
"productId": "7e2f9b14-3a6d-4c85-91f0-d4b8e5a2c763",
"force": false,
"startedAt": "2026-10-21T09:14:02.000Z",
"finishedAt": "2026-10-21T09:14:19.000Z",
"error": null,
"summary": {
"fields": 12,
"proposed": 11,
"kept": 0,
"fromData": 8,
"derived": 1,
"entered": 2,
"profile": 1,
"parts": [{ "fields": 12, "answered": true }],
"pending": ["closing_remark"]
}
},
"template": { "id": "3b1f6a52-8c0e-4f0a-9d5e-2a7c1e4b9d10", "name": "contract-letter", "fields": ["..."] }
}/v1/doc-gen/templates/proposeBody fields
true10Response fields
/v1/doc-gen/templates/{id}/confirmBody fields (all optional)
trueResponse: 201 Confirmed fields
{
"dry_run": false,
"confirmed": [
{ "key": "addressee", "label": "Addressee", "source": { "type": "product", "field": "counterparty_name" } },
{ "key": "signed_by", "label": "Signed by", "source": { "type": "user", "profile": "unterschrift" } }
]
}/v1/doc-gen/templates/{id}/rescanreason kept for each field before confirming the whole sheet, because POST /v1/doc-gen/templates/{id}/confirm without keys confirms every unconfirmed field at once. GET /v1/doc-gen/issues lists every template that still has unconfirmed fields.