Contract
Build a contract whose clauses flow and whose signature block sits at the end. You learn rich text bodies, signature, text and checkbox fields, and how to keep an item from splitting across a page break.
What this teaches
Building it in the editor
- Place a title label and add a rich text object below it. Rich text takes one block of markup and lays it out as flowing prose, so adding a clause never forces you to re-place anything.
- Write the clauses. Only allowed tags survive; others lose the tag but keep their text, and script, style and iframe are dropped whole. Know that before you paste anything in.
- Raise the line height to about 1.7. A contract is read line by line, not skimmed.
- Add a field and set its kind to signature. Name it signature — that name is the key the receiving software uses to read the value.
- Always draw a border on a signature field. Even a PDF viewer draws nothing for it, so on paper there would be nothing but blank space.
- Add two more text fields for the name and the date, and mark the name as required. Required is a flag only — we do not enforce it, and whether it is enforced is up to the program that opens the PDF.
- Add a checkbox for the acknowledgement. Put a label beside it saying what is being agreed to — a checkbox carries no text of its own.
- Put the whole signature block inside a rectangle and turn on "keep each item together" on it, so the block is never split in half by a page break.
Building it with the schema and API
- An html node takes markup in props.html. Unlike a label text the tags survive, so the validation and the failure modes differ — that is why it is a separate key.
- Inside html, {{ }} is inserted as literal text and never evaluated. If you need a value, use a label or build the markup on your server.
- A fillable box is one field node whose fieldKind picks the kind: text, check, select, list, radio, button or sign.
- You cannot pre-fill a field. To write values after signing, upload with POST /v1/files and fill with POST /v1/pdf/fields.
- Because it carries personal data, send a password with the request. We do not store passwords, so if you lose it we cannot open the file either.
After it is made
Filling values after signing
POST /v1/pdf/fields
{
"file": "file_01J…",
"values": {
"signer_name": "Do-hyun Kim",
"signed_at": "2026-09-01",
"agree": true
}
}The finished document
The JSON below is exactly the document those two routes produce. Our tests validate it and check that it renders without warnings, so you can paste it and use it as is.
{
"v": 1,
"unit": "px",
"page": {
"size": "a4",
"orientation": "portrait",
"margin": {
"t": 48,
"r": 48,
"b": 56,
"l": 48
},
"header": "",
"footer": "{{ data.title }} · [[page]] / [[pages]]"
},
"fonts": [],
"styles": [
{
"id": "s_h1",
"fontFamily": "Liberation Sans",
"fontSize": 22,
"fontWeight": 700,
"italic": false,
"color": "#111111",
"bg": "transparent",
"align": "left",
"valign": "top",
"lineHeight": 1.5,
"border": "none",
"radius": 0,
"opacity": 1
},
{
"id": "s_body",
"fontFamily": "Liberation Sans",
"fontSize": 12,
"fontWeight": 400,
"italic": false,
"color": "#222222",
"bg": "transparent",
"align": "left",
"valign": "top",
"lineHeight": 1.7,
"border": "none",
"radius": 0,
"opacity": 1
},
{
"id": "s_cap",
"fontFamily": "Liberation Sans",
"fontSize": 11,
"fontWeight": 400,
"italic": false,
"color": "#555555",
"bg": "transparent",
"align": "left",
"valign": "top",
"lineHeight": 1.5,
"border": "none",
"radius": 0,
"opacity": 1
},
{
"id": "s_field",
"fontFamily": "Liberation Sans",
"fontSize": 12,
"fontWeight": 400,
"italic": false,
"color": "#111111",
"bg": "transparent",
"align": "left",
"valign": "top",
"lineHeight": 1.5,
"border": "none",
"radius": 0,
"opacity": 1
}
],
"nodes": [
{
"id": "n_title",
"parent": "",
"order": 0,
"type": "label",
"x": 0,
"y": 0,
"w": 697,
"h": 30,
"show": "",
"styleId": "s_h1",
"props": {
"text": "{{ data.title }}",
"src": "",
"fit": "",
"thickness": 0,
"stroke": "",
"fill": "",
"repeat": "",
"as": "",
"break": "",
"keep": "",
"padding": 0,
"padY": 0,
"padX": 0,
"align": "",
"valign": "",
"wrap": "",
"bind": "",
"headerHeight": 0,
"rowHeight": 0,
"max": 0,
"columns": [],
"rows": 0,
"cells": [],
"chartKind": "",
"codeKind": "",
"ecc": "",
"fieldKind": "",
"fieldName": "",
"options": [],
"required": false,
"readonly": false,
"html": ""
},
"name": "Title"
},
{
"id": "n_body",
"parent": "",
"order": 1,
"type": "html",
"x": 0,
"y": 48,
"w": 697,
"h": 0,
"show": "",
"styleId": "s_body",
"props": {
"text": "",
"src": "",
"fit": "",
"thickness": 0,
"stroke": "",
"fill": "",
"repeat": "",
"as": "",
"break": "",
"keep": "",
"padding": 0,
"padY": 0,
"padX": 0,
"align": "",
"valign": "",
"wrap": "",
"bind": "",
"headerHeight": 0,
"rowHeight": 0,
"max": 0,
"columns": [],
"rows": 0,
"cells": [],
"chartKind": "",
"codeKind": "",
"ecc": "",
"fieldKind": "",
"fieldName": "",
"options": [],
"required": false,
"readonly": false,
"html": "<p>This agreement is entered into between the Client and the Supplier for the provision of services on the terms below.</p><h3>1. Purpose</h3><p>This agreement sets out the scope of the services the Supplier provides to the Client and the fee payable for them.</p><h3>2. Term</h3><p>The term is six months from the commencement date.</p><h3>3. Payment</h3><ul><li>30% on commencement</li><li>40% at the midpoint</li><li>30% on completion</li></ul>"
},
"name": "Clauses"
},
{
"id": "n_signbox",
"parent": "",
"order": 2,
"type": "rect",
"x": 0,
"y": 420,
"w": 697,
"h": 140,
"show": "",
"styleId": "s_cap",
"props": {
"text": "",
"src": "",
"fit": "",
"thickness": 0,
"stroke": "",
"fill": "",
"repeat": "",
"as": "",
"break": "",
"keep": "together",
"padding": 0,
"padY": 0,
"padX": 0,
"align": "",
"valign": "",
"wrap": "",
"bind": "",
"headerHeight": 0,
"rowHeight": 0,
"max": 0,
"columns": [],
"rows": 0,
"cells": [],
"chartKind": "",
"codeKind": "",
"ecc": "",
"fieldKind": "",
"fieldName": "",
"options": [],
"required": false,
"readonly": false,
"html": ""
},
"name": "Signature block"
},
{
"id": "n_agree",
"parent": "n_signbox",
"order": 3,
"type": "field",
"x": 12,
"y": 12,
"w": 18,
"h": 18,
"show": "",
"styleId": "s_field",
"props": {
"text": "",
"src": "",
"fit": "",
"thickness": 1,
"stroke": "#111111",
"fill": "",
"repeat": "",
"as": "",
"break": "",
"keep": "",
"padding": 0,
"padY": 0,
"padX": 0,
"align": "",
"valign": "",
"wrap": "",
"bind": "",
"headerHeight": 0,
"rowHeight": 0,
"max": 0,
"columns": [],
"rows": 0,
"cells": [],
"chartKind": "",
"codeKind": "",
"ecc": "",
"fieldKind": "check",
"fieldName": "agree",
"options": [],
"required": false,
"readonly": false,
"html": ""
},
"name": "Consent box"
},
{
"id": "n_agree_l",
"parent": "n_signbox",
"order": 4,
"type": "label",
"x": 38,
"y": 12,
"w": 400,
"h": 18,
"show": "",
"styleId": "s_cap",
"props": {
"text": "I have read and understood the clauses above and agree to them.",
"src": "",
"fit": "",
"thickness": 0,
"stroke": "",
"fill": "",
"repeat": "",
"as": "",
"break": "",
"keep": "",
"padding": 0,
"padY": 0,
"padX": 0,
"align": "",
"valign": "",
"wrap": "",
"bind": "",
"headerHeight": 0,
"rowHeight": 0,
"max": 0,
"columns": [],
"rows": 0,
"cells": [],
"chartKind": "",
"codeKind": "",
"ecc": "",
"fieldKind": "",
"fieldName": "",
"options": [],
"required": false,
"readonly": false,
"html": ""
},
"name": "Consent line"
},
{
"id": "n_name",
"parent": "n_signbox",
"order": 5,
"type": "field",
"x": 12,
"y": 52,
"w": 260,
"h": 28,
"show": "",
"styleId": "s_field",
"props": {
"text": "",
"src": "",
"fit": "",
"thickness": 1,
"stroke": "#8a93a0",
"fill": "",
"repeat": "",
"as": "",
"break": "",
"keep": "",
"padding": 0,
"padY": 0,
"padX": 0,
"align": "",
"valign": "",
"wrap": "",
"bind": "",
"headerHeight": 0,
"rowHeight": 0,
"max": 0,
"columns": [],
"rows": 0,
"cells": [],
"chartKind": "",
"codeKind": "",
"ecc": "",
"fieldKind": "text",
"fieldName": "signer_name",
"options": [],
"required": true,
"readonly": false,
"html": ""
},
"name": "Name field"
},
{
"id": "n_name_l",
"parent": "n_signbox",
"order": 6,
"type": "label",
"x": 12,
"y": 84,
"w": 260,
"h": 16,
"show": "",
"styleId": "s_cap",
"props": {
"text": "Full name (required)",
"src": "",
"fit": "",
"thickness": 0,
"stroke": "",
"fill": "",
"repeat": "",
"as": "",
"break": "",
"keep": "",
"padding": 0,
"padY": 0,
"padX": 0,
"align": "",
"valign": "",
"wrap": "",
"bind": "",
"headerHeight": 0,
"rowHeight": 0,
"max": 0,
"columns": [],
"rows": 0,
"cells": [],
"chartKind": "",
"codeKind": "",
"ecc": "",
"fieldKind": "",
"fieldName": "",
"options": [],
"required": false,
"readonly": false,
"html": ""
},
"name": "Name caption"
},
{
"id": "n_date",
"parent": "n_signbox",
"order": 7,
"type": "field",
"x": 292,
"y": 52,
"w": 160,
"h": 28,
"show": "",
"styleId": "s_field",
"props": {
"text": "",
"src": "",
"fit": "",
"thickness": 1,
"stroke": "#8a93a0",
"fill": "",
"repeat": "",
"as": "",
"break": "",
"keep": "",
"padding": 0,
"padY": 0,
"padX": 0,
"align": "",
"valign": "",
"wrap": "",
"bind": "",
"headerHeight": 0,
"rowHeight": 0,
"max": 0,
"columns": [],
"rows": 0,
"cells": [],
"chartKind": "",
"codeKind": "",
"ecc": "",
"fieldKind": "text",
"fieldName": "signed_at",
"options": [],
"required": false,
"readonly": false,
"html": ""
},
"name": "Date field"
},
{
"id": "n_date_l",
"parent": "n_signbox",
"order": 8,
"type": "label",
"x": 292,
"y": 84,
"w": 160,
"h": 16,
"show": "",
"styleId": "s_cap",
"props": {
"text": "Date signed",
"src": "",
"fit": "",
"thickness": 0,
"stroke": "",
"fill": "",
"repeat": "",
"as": "",
"break": "",
"keep": "",
"padding": 0,
"padY": 0,
"padX": 0,
"align": "",
"valign": "",
"wrap": "",
"bind": "",
"headerHeight": 0,
"rowHeight": 0,
"max": 0,
"columns": [],
"rows": 0,
"cells": [],
"chartKind": "",
"codeKind": "",
"ecc": "",
"fieldKind": "",
"fieldName": "",
"options": [],
"required": false,
"readonly": false,
"html": ""
},
"name": "Date caption"
},
{
"id": "n_sign",
"parent": "n_signbox",
"order": 9,
"type": "field",
"x": 472,
"y": 52,
"w": 213,
"h": 56,
"show": "",
"styleId": "s_field",
"props": {
"text": "",
"src": "",
"fit": "",
"thickness": 1,
"stroke": "#8a93a0",
"fill": "",
"repeat": "",
"as": "",
"break": "",
"keep": "",
"padding": 0,
"padY": 0,
"padX": 0,
"align": "",
"valign": "",
"wrap": "",
"bind": "",
"headerHeight": 0,
"rowHeight": 0,
"max": 0,
"columns": [],
"rows": 0,
"cells": [],
"chartKind": "",
"codeKind": "",
"ecc": "",
"fieldKind": "sign",
"fieldName": "signature",
"options": [],
"required": false,
"readonly": false,
"html": ""
},
"name": "Signature field"
},
{
"id": "n_sign_l",
"parent": "n_signbox",
"order": 10,
"type": "label",
"x": 472,
"y": 112,
"w": 213,
"h": 16,
"show": "",
"styleId": "s_cap",
"props": {
"text": "Signature",
"src": "",
"fit": "",
"thickness": 0,
"stroke": "",
"fill": "",
"repeat": "",
"as": "",
"break": "",
"keep": "",
"padding": 0,
"padY": 0,
"padX": 0,
"align": "",
"valign": "",
"wrap": "",
"bind": "",
"headerHeight": 0,
"rowHeight": 0,
"max": 0,
"columns": [],
"rows": 0,
"cells": [],
"chartKind": "",
"codeKind": "",
"ecc": "",
"fieldKind": "",
"fieldName": "",
"options": [],
"required": false,
"readonly": false,
"html": ""
},
"name": "Signature caption"
}
]
}The request body. Put the document above into template and the data below into data.
{
"template": "(the document above)",
"data": {
"title": "Services agreement"
},
"password": "{{open password for this contract}}",
"permissions": {
"modify": false,
"copy": false
}
}