TEMPLATES · DOCUMENT TESTS
Break the layout before production
A template is designed once against one friendly payload. Then real data hits it forever — the 94-character legal name, the 60-row order, the six-line address, the emoji in a product name. The layout breaks, the API still returns 200, and the broken PDF goes straight to your customer's customer. Document tests generate those payloads automatically from each template's typed contract, render them all, and report what broke.
Start a run
POST /v1/templates/:id/tests returns 202 with a run_id. Poll GET /v1/templates/:id/tests/:run_id until status is passed, failed, or error.
curl -X POST https://doclinth.com/v1/templates/tmpl_…/tests \
-H "Authorization: Bearer dl_live_YOUR_KEY"{
"id": "run_…",
"status": "failed",
"variants_total": 11,
"variants_failed": 1,
"results": [
{
"name": "long_strings",
"kind": "auto",
"status": "fail",
"duration_ms": 1840,
"diagnostics": {
"messages": ["Text clipped in .customer-name: The International Consortium for…"]
},
"artifact_url": "https://…/tests/…/auto-long_strings.pdf"
}
]
}A failing variant stores its rendered (TEST-watermarked) PDF for 24 hours; each result carries a short, signed artifact_url so you can see exactly what broke. Passing variants store diagnostics only.
Automatic variants
Every variant is complete — all required fields present — so strict mode is never what fails; only the layout is under test. Each variant mutates one dimension, so a failure points to a specific pressure. If baseline fails, the run aborts: the template itself is broken and everything else would be noise.
| Variant | Pressure |
|---|---|
baseline | The template's own sample data, untouched (control). |
empty_arrays | Every list becomes empty. |
single_row | Every list is one row. |
many_rows | Top-level lists grow to 60 rows — page breaks, repeating headers. |
long_strings | Text fields grow ~5× — the long company name, the wrapping address. |
unbroken_token | A 120-character unbroken URL/ID that can force horizontal overflow. |
unicode | CJK, Arabic (RTL), and emoji mixed into text fields. |
missing_optionals | Every {{#if}}-guarded optional field omitted. |
extreme_numbers | Very large and negative numbers in numeric fields. |
broken_images | Image URLs point nowhere — does the layout survive a missing logo? |
Structured values a helper interprets — a currency code, a barcode payload, a date — are kept valid across every variant (they are contract fields, not free text), so a run tests the layout, not the helper's input validation.
What counts as a failure
Only high-confidence checks — a stress-tester that cries wolf is worse than none:
- The document is wider than the printable page (horizontal overflow).
- Text is clipped inside an element that hides its own overflow.
- An image failed to load.
- An unresolved
{{template marker survived into the PDF. - A page rendered blank.
- A fixture expectation missed: wrong page count, required text absent, forbidden text present, or the render was too slow.
Fixtures add your own edge cases
Alongside the automatic variants, every named fixture runs with its expectations checked — your real 60-line order, your longest customer name, the payload that broke once in production. Publishing a template auto-seeds a sample fixture, so tests have a baseline from day one.
From the SDK and agents
// Node — @doclinth/sdk
const run = await doclinth.runTemplateTests("tmpl_…");
const done = await doclinth.getTemplateTestRun("tmpl_…", run.id);Over MCP, the run_template_teststool queues a run and returns a summary with failure diagnostics — so an agent can create a template, stress-test it, and iterate until it's green. get_template includes the latest run summary.
Limits
Test renders do not consume your monthly generation quota — never pay to test safely. Runs are capped per template per day (free 4, paid 20) and rate-limited per account. Failing artifacts expire on the 24-hour purge.
No account? Try one first
Paste a template into the free PDF stress tester — no signup for the first run. It runs the same engine on a capped variant set and returns a shareable report.