Document model
A kove document is a JSON object. You describe what you want (a header, a table, some totals) and kove decides how it looks and how it splits across pages. You never write CSS or page-break: you just declare the content.
Root structure
Section titled “Root structure”{ "page": { "size": "A4", "margin": "18mm" }, "header": { "repeat": true, "text": "My company · Document" }, "footer": { "repeat": true, "pageNumbers": "Page {page} of {pages}" }, "body": [ /* blocks in order */ ]}| Field | Type | Required | Description |
|---|---|---|---|
page |
object | no | Page size and margins. |
header |
object | no | Running header (repeats on every page). |
footer |
object | no | Running footer with page numbers. |
body |
block[] |
yes | The list of document blocks, in order. |
Only body is required. Everything else has sensible defaults.
Page, header, and footer
Section titled “Page, header, and footer”Defines the canvas. size takes A4 (default), Letter, Legal, A5, A3. margin is a CSS length (for example 18mm, default 18mm).
{ "size": "A4", "margin": "18mm" }header · running header
Section titled “header · running header”Text that repeats at the top of every page, so you do not have to repeat it yourself. That is what it gives you: the document identity on each sheet.
{ "repeat": true, "text": "Acme Inc. · Invoice F-2026-001" }The text is escaped (no raw HTML, for safety). repeat defaults to true.
footer · running footer with page numbers
Section titled “footer · running footer with page numbers”A footer repeated on every page with a page-number template. pageNumbers takes {page} and {pages}.
{ "repeat": true, "pageNumbers": "Page {page} of {pages}" }This gives you “Page X of Y” without computing anything: kove knows the total page count after it lays out the pages.
Block building blocks
Section titled “Block building blocks”Each item in body (and in section.body) is a block with a type. These are all of them.
heading · title
Section titled “heading · title”{ "type": "heading", "text": "Period summary", "level": 2 }level can be 1 (default), 2, or 3. A heading is never left alone at the bottom of a page: kove keeps it with the content that follows, so you do not get stranded titles.
text · paragraph
Section titled “text · paragraph”{ "type": "text", "text": "Payment due 30 days from the issue date." }Regular text. It flows and splits across pages naturally.
fields · label/value pairs
Section titled “fields · label/value pairs”Document metadata (customer, date, tax ID, and so on) as a list of pairs.
{ "type": "fields", "items": [ { "label": "Customer", "value": "Globex Corp" }, { "label": "Date", "value": "2026-06-29" }, { "label": "Tax ID", "value": "B-12345678" } ]}Each item needs a label and a value (both strings). This gives you the usual invoice or quote header block without laying out a table by hand.
table · paginated table
Section titled “table · paginated table”The main block. Typed columns and rows as objects keyed by key.
{ "type": "table", "repeatHeader": true, "keepRowTogether": true, "columns": [ { "key": "desc", "label": "Description" }, { "key": "qty", "label": "Qty", "align": "right" }, { "key": "total", "label": "Total", "align": "right" } ], "rows": [ { "desc": "Brand design", "qty": 1, "total": "1200.00" }, { "desc": "Support (hours)", "qty": 8, "total": "480.00" } ]}Each column has a key, a label, and an optional align (left / center / right). Rows are objects whose keys match the column key values.
What the table handles for you across pages:
repeatHeader(defaulttrue): the header row repeats on every page the table covers. A 200-row table does not leave you guessing on page 4.keepRowTogether(defaulttrue): a row is never split across two pages.
totals · totals block
Section titled “totals · totals block”The subtotal / tax / total lines, usually at the end of a table.
{ "type": "totals", "keepWithPrevious": true, "lines": [ { "label": "Subtotal", "value": "$2880.00" }, { "label": "Tax 21%", "value": "$604.80" }, { "label": "Total", "value": "$3484.80", "emphasis": true } ]}Each line has a label, a value, and an optional emphasis (default false, to highlight the total). keepWithPrevious (default true) keeps the totals from being stranded on a new page, away from the table they belong to.
section · titled group
Section titled “section · titled group”Groups blocks under a title, optionally keeping them together.
{ "type": "section", "title": "Terms", "keepTogether": true, "body": [ { "type": "text", "text": "Payment due 30 days from issue." }, { "type": "text", "text": "Late fee of 1% per month." } ]}body is a list of nested blocks (the same building blocks). With keepTogether: true (default false) the whole section does not split across pages, which is useful for legal clauses or boxes that should be seen as one piece.
spacer · vertical space
Section titled “spacer · vertical space”{ "type": "spacer", "size": "10mm" }size is a CSS length (default 8mm). Controlled white space between blocks.
divider · separator line
Section titled “divider · separator line”{ "type": "divider" }A horizontal rule. No options.
pageBreak · explicit page break
Section titled “pageBreak · explicit page break”{ "type": "pageBreak" }Forces the start of a new page. You decide the break, not the browser.
image · image
Section titled “image · image”{ "type": "image", "src": "https://cdn.acme.com/logo.png", "width": "40mm", "align": "center", "alt": "Acme logo" }src must be an https URL or a data URI (file:// and http:// are not allowed: kove fetches the resource, so this is for safety). width (CSS length), align (left/center/right), and alt are optional.
signature · signature block
Section titled “signature · signature block”A visual signature box (line + name + label).
{ "type": "signature", "name": "Globex Corp", "label": "Approved · sign and stamp" }name and label are optional.
Full example
Section titled “Full example”You can see all of this in one real document (an invoice with a long table, totals, terms, and a signature) in the Quickstart. To use it, see HTTP API, CLI, or MCP.