Data contract
A template is not just a layout: it declares the variables it reads — names, types, whether they repeat per record, and example values. That declaration is the data contract between the template’s designer and the system that calls the API.
Fetch the contract
Section titled “Fetch the contract”curl https://composepdf.com/v1/canvases/{canvasId}/schema \ -H "x-api-key: $COMPOSEPDF_API_KEY"The response is a JSON Schema describing the payload for
POST /v1/canvases/{canvasId}/pdf — generate types from it, validate in CI,
or build a form against it.
Enforcement
Section titled “Enforcement”The same contract is checked server-side before layout. A payload that
does not satisfy it never reaches rendering; the API answers 400 with the
exact violating paths:
{ "error": "data does not satisfy the template's declarations", "violations": ["customer.name", "items[2].price"]}With dataList, paths name the record: dataList[3].customer.name.
Why contracts instead of “render whatever arrives”
Section titled “Why contracts instead of “render whatever arrives””A wrong payload that renders something is the worst failure mode: the
mistake surfaces as a blank field on a customer-facing document, weeks later.
The contract moves that failure to the API boundary, where your integration
tests can see it — a 400 today instead of an empty invoice in production.
Enum-typed variables are part of the contract too: a value outside the declared set is a violation, and the schema publishes the allowed members so your form can render them as options.