Let business users edit content
Content fields are the parts of a template that a content editor may change in Studio without seeing its code: a logo, an accent colour, payment terms, a closing line. A developer marks them once. A content editor then fills them in a form, previews the document, and publishes it.
Mark the fields
Section titled “Mark the fields”Open the template in Workspace → Editor and choose + → Add fields.json at the end of the tab bar under its source. List the fields there:
[ { "key": "accent", "label": "Accent colour", "type": "colour", "group": "Brand" }, { "key": "logo", "label": "Logo", "type": "image", "group": "Brand" }, { "key": "terms", "label": "Payment terms", "type": "richText", "group": "Footer" }]| Property | What it does |
|---|---|
key |
Required. The name the template reads it by: a letter, then letters, digits or _, up to 64. |
label |
Required. What the form shows. |
type |
Required. One of the types below. |
group |
Fields with the same group are shown together under it. |
help |
A hint shown under the input. |
multiline |
A text field that holds several lines. |
maxLength |
The most characters a text or richText value holds. |
options |
Required for choice, and only allowed there: the values it offers. |
type |
Value | Default limit |
|---|---|---|
text |
Plain text, one line unless multiline |
2,000 characters |
richText |
Paragraphs, bold, italic, links and bulleted or numbered lists, as HTML | 20,000 characters |
image |
An uploaded PNG, JPEG or SVG | |
colour |
A colour such as #1f4fd1 |
|
toggle |
On or off | |
choice |
One of options |
|
number |
A number | |
date |
A date such as 2026-10-16 |
A template has up to 100 fields, and their values take up to 64 KB together. The Invoice example template comes with an accent colour, a logo and payment details already marked.
Use them in the template
Section titled “Use them in the template”The values are in the data as $content:
<style> h1 { color:{{$content.accent}}; }</style>{{#if $content.logo}}<img src="{{$content.logo}}" alt="" />{{/if}}<h1>Invoice</h1>{{#each items}} <p style="border-color: {{@root.$content.accent}}">{{description}}</p>{{/each}}<footer>{{{$content.terms}}}</footer>- Write rich text with three braces,
{{{$content.terms}}}, so its formatting renders. Whatever is typed or pasted, only paragraphs, line breaks, bold, italic, links (http,httpsandmailto) and lists are kept. - Inside
{{#each}}and other blocks, read the values as@root.$content.key. - An
imagevalue is the uploaded image’s ID; use it as ansrcas above. - A field without a value renders as empty.
- The content.json tab holds the values. The Bindings tab and autocomplete list them.
- Request data cannot set
$content: a render or preview whose data has a$contentkey answers400 BAD_REQUEST.
Publish the template once yourself. A content editor can only publish a template that has been published before.
Invite a content editor
Section titled “Invite a content editor”In Manage → Team, invite the person with the role Content editor, or select someone and Change role. A content editor sees only Content in Studio and can:
- change the values of content fields, preview the document with sample data, and publish it;
- upload images for image fields;
- read templates, render them, and read the workspace’s renders.
They cannot change a template’s code, its sample data or workspace files, and cannot see API keys, webhooks, the team or the audit log.
Edit content
Section titled “Edit content”A content editor opens Content, which lists the templates that have content fields and whether their changes are published, and picks one:
- Change the fields in the form, and click Update preview to see the document with the template’s sample data. For a template that fills an Excel workbook, click Download spreadsheet instead.
- Click Save. Each save is kept as a revision; History shows the earlier versions and what each changed, and Use this version loads one back into the form.
- Click Publish. Documents render with the new values from then on.
A content editor’s publish never ships a developer’s unfinished work. It is refused with
403 FORBIDDEN while the draft has changes other than content values that are not published yet;
a developer publishes those first.
When two people change the same template, a save keeps what the other saved in the meantime:
- Two content editors: each save keeps the other’s values and changes only the fields this editor changed.
- A content editor and a developer with the template open: the developer’s next save keeps the content editor’s values, unless the developer changed content.json too.
A value left over from a field a developer removed is dropped at the next save.
Over HTTP
Section titled “Over HTTP”Save content values with PUT /v1/templates/{id}/content and the template:content scope, plus
template:read. It replaces the values on the latest draft and keeps everything else:
curl -X PUT "$PDF_URL/v1/templates/$TEMPLATE_ID/content" \ -H "x-api-key: $API_KEY" \ -H "content-type: application/json" \ -d '{"values":{"accent":"#1f4fd1","terms":"<p>Due in <strong>30 days</strong>.</p>"}}'- Values must fit their fields; a key no field declares, or a value of the wrong kind, answers
400 BAD_REQUEST. A template without content fields answers400too. - Send
"expectedRevision"to refuse the change with409 CONFLICTif the draft has changed since you read it. GET /v1/templates?content=truelists the templates that have content fields. Each template carriespublishedRevision, the draft revision its live version was published from.- Publishing with
template:publishbut withouttemplate:writefollows the rule above. - Uploading an image for an image field needs
template:contentorasset:manage.
Each save is recorded in the audit log as
template.content_updated, with the fields it changed. A workspace
export carries fields.json and content.json, and importing it
replaces the values in the target environment.