Multipart mode
POST /render with Content-Type: multipart/form-data.
Fields
| Field | Required | Repeatable | Purpose |
|---|---|---|---|
template | yes | no | The entry-point .typ source. Filename becomes the in-bundle path. |
inputs | no | no | JSON document. Exposed to the template as sys.inputs.data (stringified). |
assets | no | yes | Any additional file (image, .typ for imports, data file). Filename = path. |
Response
- 200 — body is the compiled PDF,
Content-Type: application/pdf. - 400 — malformed request: missing
template, invalidinputsJSON, unknown field, etc. - 422 — compile error. Body is the Typst diagnostic.
- 408 — compile took longer than the configured timeout. The worker was killed.
- 413 — multipart body or resulting PDF exceeded the configured size limit.
- 503 — server at its concurrent-compile limit. Retry later.
Reading inputs in the template
The inputs field is stringified and exposed as sys.inputs.data. Decode it once at the top of the template:
#let d = json.decode(sys.inputs.data)
= Invoice for #d.customer
#table(
columns: 2,
..for line in d.line_items {
(line.description, str(line.amount))
}
)
This single-key contract keeps the protocol simple: arbitrary JSON in, arbitrary structure available in the template.
Imports across multiple files
Send each additional .typ file as an assets field. The filename becomes the path used in #import:
curl -sS -o out.pdf \
-X POST https://typst.floomatik.com/render \
-F "template=@main.typ" \
-F "assets=@components/header.typ;filename=header.typ" \
-F "assets=@components/footer.typ;filename=footer.typ"
// main.typ
#import "header.typ": page_header
#import "footer.typ": page_footer
#page_header()
= Body content
#page_footer()
Images
Same mechanism. Send each image as an assets field; reference by filename in image():
curl -sS -o report.pdf \
-X POST https://typst.floomatik.com/render \
-F "template=@report.typ" \
-F "assets=@brand/logo.png" \
-F "assets=@charts/q4.svg"
#image("logo.png", width: 40%)
== Q4 Results
#image("q4.svg")
SVG and PNG are both supported (Typst handles raster and vector). Asset bytes go through verbatim — no transcoding.
Error responses are plain text
A 422 from a broken template looks like:
compile failed: unknown variable: name
Suitable for logging or surfacing back to a UI.