Skip to main content

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.

POST/v1/doc-gen/templates/{id}/propose

Body fields

data_product_id*uuidThe data product whose columns (and bound reference table) the sources are proposed from.
forcebooleanStart a fresh proposal even if the sheet was already proposed for the product.
GET/v1/doc-gen/templates/{id}/proposal

Proposal fields

statusstringrunning, done or failed.
runIdstringIdentifies one run. Absent on proposals stored before runs had an identity.
productId, forceuuid, booleanThe data product the run reads, and whether it was a forced fresh start.
onlystring[]Set when the run only asks again for fields an earlier run left without an answer.
startedAt, finishedAt, errormixedRun timing (finishedAt is null while running) and the failure message of a failed run.
summaryobject | nullfields, proposed, kept, fromData, derived, entered, profile (counts), model, parts[] (one entry per model call with fields, answered, note) and pending[] (keys of fields no answer came back for).

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": ["..."] }
}
POST/v1/doc-gen/templates/propose

Body fields

data_product_id*uuidThe data product to propose against.
template_idsuuid[]Up to 200 templates. All templates when omitted or empty.
only_unproposedbooleanOnly templates not yet proposed for this product. Default: true
forcebooleanStart fresh proposals even for sheets already proposed for the product.
limitintegerMost templates to start, 1 to 10. Default: 10
dry_runbooleanStart nothing; name the templates that would start in would_start.

Response fields

started[]arrayPer started proposal: id, name, run_id (nullable) and status (running, done or failed). Set when dry_run is not true.
would_start[]arrayWith dry_run: id, name and fields (the field count) of each template that would start.
skippedintegerCandidate templates left out because of limit.
dry_runbooleanEchoes the request flag.
POST/v1/doc-gen/templates/{id}/confirm

Body fields (all optional)

keysstring[]Up to 500 fields to confirm. Every unconfirmed field when omitted or empty.
include_enteredbooleanWithout keys, also confirm the fields entered per document. When false they are left out. Default: true
dry_runbooleanList what would be confirmed and store nothing.

Response: 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" } }
  ]
}
POST/v1/doc-gen/templates/{id}/rescan
Confirmation is the point at which a suggestion becomes a decision. Review the reason 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.

Frequently asked questions

Why did my proposal leave fields pending?+
A part of the model run gave no answer for those fields. They are listed in summary.pending, they keep their sources only where those read this product, and the next proposal for the same product asks for exactly those fields again. GET /v1/doc-gen/issues reports them as proposal_pending.
Does a proposal overwrite fields a person already confirmed?+
No. A confirmed field is left as it is on an ordinary run. On a forced fresh start it is also kept when the person changed it while the run was going. Only unconfirmed fields receive new suggestions.
What does 409 on a propose request mean?+
Another proposal started for the template at the same moment. The conflict is retryable: repeat the request, which then joins the running run when it is for the same product and mode.
Why is dataProductId null on a template?+
It is set when a proposal answered every field for a data product, and stays null until then. A template with a null dataProductId appears as never_proposed in the issues worklist.