Skip to main content

Inputs

Endpoints for managing a Template's inputs. Inputs define the parameters a user supplies when rendering a Template (a report title, a date range, a region, and so on).

Every input route runs template.ensureTemplates, which asserts the templates license feature, and resolves the parent Template through the read_access model scope, so a caller only ever operates on inputs of a Template it may see. Read routes (list, get) need only an authenticated user; the write routes layer on a Template permission as noted per endpoint.

Input model

Each input is a row owned by its Template. Its key columns:

FieldTypeDescription
idstring (UUID)Input id
templateIdstring (UUID)Owning Template id
namestringVariable name used in the Template and in lock/default maps
labelstring | nullDisplay label shown to users
driverstringInput-driver id (see below)
dataTypestringRegistered dataType-driver id (for example keyword_text)
defnobject | nullDriver-specific configuration
defaultValueany | nullDefault value
helpstring | nullHelp text shown to users
requiredbooleanWhether a value is required (default false)
multiplebooleanWhether multiple values are allowed (default false)
indexintegerPosition in the input list (server-assigned)
editorComponentobjectServer-derived editor descriptor keyed by the input driver
createdAt / updatedAtdateTimestamps
driver and dataType are registered driver ids

driver is an input-driver id from the input driver manager, not a loose name. Valid values include mdMultiInput, mdSelect, mdRadioGroup, mdDatepicker, mdCheckbox, mdSwitch, and mdSlider. dataType is a registered dataType-driver id such as keyword_text. The model validates both against the registered driver keys, so values like text, select, or string answer 400.


GET /api/templates/{id}/inputs

List a Template's inputs.

Authentication: Required

Pre-blocks: template.lookup(params.id)

Response:

A HAL collection embedding inf:template-input, queried for this templateId. Each embedded input carries the columns described above.

List a template’s inputsGET /api/templates/00000000-0000-4000-8000-000000000001/inputs
GET /api/templates/{id}/inputs is a HAL collection embedding inf:template-input (db.query filtered to this templateId). Each input carries driver/dataType/name/label/required/multiple/defaultValue/help/index/defn. `driver` is an input-driver id (mdMultiInput, mdSelect, mdDatepicker, ...) and `dataType` is a registered dataType (keyword_text, ...).
Response · 200
{
"_links": {
"self": {
"href": "https://informer.example.com/api/templates/00000000-0000-4000-8000-000000000001/inputs"
}
},
"start": 0,
"count": 3,
"total": 3,
"_embedded": {
"inf:template-input": [
{
"_links": {
"self": {
"href": "https://informer.example.com/api/templates/00000000-0000-4000-8000-000000000001/inputs/00000000-0000-4000-8000-000000000002"
}
},
"editorComponent": {
"mdMultiInput": {}
},
"tenant": "acme",
"id": "00000000-0000-4000-8000-000000000002",
"driver": "mdMultiInput",
"dataType": "keyword_text",
"defn": null,
"name": "report_title",
"label": "Report Title",
"help": "Enter the title for this report",
"defaultValue": "Monthly Report",
"required": true,
"multiple": false,
"index": 0,
"templateId": "00000000-0000-4000-8000-000000000001",
"createdAt": "2026-01-15T16:20:00.000Z",
"updatedAt": "2026-01-15T16:20:00.000Z"
},
{
"_links": {
"self": {
"href": "https://informer.example.com/api/templates/00000000-0000-4000-8000-000000000001/inputs/00000000-0000-4000-8000-000000000003"
}
},
"editorComponent": {
"mdSelect": {}
},
"tenant": "acme",
"id": "00000000-0000-4000-8000-000000000003",
"driver": "mdSelect",
"dataType": "keyword_text",
"defn": null,
"name": "region",
"label": "Region",
"help": null,
"defaultValue": null,
"required": false,
"multiple": false,
"index": 1,
"templateId": "00000000-0000-4000-8000-000000000001",
"createdAt": "2026-01-15T16:20:00.000Z",
"updatedAt": "2026-01-15T16:20:00.000Z"
},
{
"_links": {
"self": {
"href": "https://informer.example.com/api/templates/00000000-0000-4000-8000-000000000001/inputs/00000000-0000-4000-8000-000000000004"
}
},
"editorComponent": {
"mdMultiInput": {}
},
"tenant": "acme",
"id": "00000000-0000-4000-8000-000000000004",
"driver": "mdMultiInput",
"dataType": "keyword_text",
"defn": null,
"name": "scratch_input",
"label": "Scratch Input",
"help": null,
"defaultValue": null,
"required": false,
"multiple": false,
"index": 2,
"templateId": "00000000-0000-4000-8000-000000000001",
"createdAt": "2026-01-15T16:20:00.000Z",
"updatedAt": "2026-01-15T16:20:00.000Z"
}
]
}
}
Captured from the API examples test suite; ids and timestamps are normalized.

POST /api/templates/{id}/inputs

Add an input to a Template.

Authentication: Required

Pre-blocks: template.lookup(params.id), permission.template.write(pre.template) (Designer)

Request Body:

FieldTypeRequiredDescription
dataTypestringYesRegistered dataType-driver id (for example keyword_text)
driverstringYesInput-driver id (for example mdMultiInput, mdSelect, mdDatepicker)
namestringYesVariable name used in the Template
labelstringYesDisplay label for users
defnobjectNoDriver-specific configuration
helpstring | nullNoHelp text ("" or null allowed)
defaultValueanyNoDefault value
requiredbooleanNoMark as required (default false)
multiplebooleanNoAllow multiple values (default false)

index is server-assigned to the current input count, so any index sent in the payload is ignored.

Example Request:

{
"dataType": "keyword_text",
"driver": "mdMultiInput",
"name": "customer_name",
"label": "Customer Name",
"required": false,
"help": "Optional customer filter"
}

Response:

Responds 201 Created with the created input and a Location header.

Add an input to a templatePOST /api/templates/00000000-0000-4000-8000-000000000001/inputs
POST /api/templates/{id}/inputs requires template:write (Designer). Payload is { dataType, driver, name, label, defn?, help?, defaultValue?, required?, multiple? } — dataType and driver are validated against the registered dataType/input driver ids (the .md’s text/select/string values 400). `index` is server-assigned to the current input count. Responds 201 with a Location header.
Request body
{
"dataType": "keyword_text",
"driver": "mdMultiInput",
"name": "customer_name",
"label": "Customer Name",
"required": false,
"help": "Optional customer filter"
}
Response · 201
{
"_links": {
"self": {
"href": "https://informer.example.com/api/templates/00000000-0000-4000-8000-000000000001/inputs/00000000-0000-4000-8000-000000000002"
}
},
"editorComponent": {
"mdMultiInput": {}
},
"tenant": "acme",
"id": "00000000-0000-4000-8000-000000000002",
"dataType": "keyword_text",
"driver": "mdMultiInput",
"name": "customer_name",
"label": "Customer Name",
"required": false,
"help": "Optional customer filter",
"multiple": false,
"templateId": "00000000-0000-4000-8000-000000000001",
"index": 3,
"updatedAt": "2026-01-15T16:20:00.000Z",
"createdAt": "2026-01-15T16:20:00.000Z",
"defn": null,
"defaultValue": null
}
Captured from the API examples test suite; ids and timestamps are normalized.

GET /api/templates/{id}/inputs/{input}

Get a single input.

Authentication: Required

Pre-blocks: template.lookup(params.id), then an input lookup scoped to the Template id and input id (a missing id answers 404)

Response:

Get a single inputGET /api/templates/00000000-0000-4000-8000-000000000001/inputs/00000000-0000-4000-8000-000000000002
GET /api/templates/{id}/inputs/{input} is a db.lookup scoped to templateId + input id; a missing id 404s. Returns the input columns.
Response · 200
{
"_links": {
"self": {
"href": "https://informer.example.com/api/templates/00000000-0000-4000-8000-000000000001/inputs/00000000-0000-4000-8000-000000000002"
}
},
"editorComponent": {
"mdMultiInput": {}
},
"tenant": "acme",
"id": "00000000-0000-4000-8000-000000000002",
"driver": "mdMultiInput",
"dataType": "keyword_text",
"defn": null,
"name": "report_title",
"label": "Report Title",
"help": "Enter the title for this report",
"defaultValue": "Monthly Report",
"required": true,
"multiple": false,
"index": 0,
"templateId": "00000000-0000-4000-8000-000000000001",
"createdAt": "2026-01-15T16:20:00.000Z",
"updatedAt": "2026-01-15T16:20:00.000Z"
}
Captured from the API examples test suite; ids and timestamps are normalized.

PUT /api/templates/{id}/inputs/{input}

Update an input's configuration.

Authentication: Required

Pre-blocks: template.lookup(params.id), permission.template.write(pre.template) (Designer)

Request Body:

The payload is stripUnknown. Note that the update payload differs from the create payload: name is required, dataType is not accepted here (it is dropped), and driver/label accept null.

FieldTypeRequiredDescription
namestringYesVariable name
driverstring | nullNoInput-driver id
defnobject | nullNoDriver-specific configuration
labelstring | nullNoDisplay label
helpstring | nullNoHelp text ("" allowed)
defaultValueany | nullNoDefault value
requiredbooleanNoMark as required (default false)
multiplebooleanNoAllow multiple values (default false)
dataType cannot be changed here

dataType is dropped from the update payload. Omitting required or multiple resets them to false rather than preserving the prior value.

Example Request:

{
"name": "report_title",
"label": "Custom Report Title",
"defaultValue": "Q1 Report",
"required": false
}

Response:

Responds 200 with the updated input.

Update an inputPUT /api/templates/00000000-0000-4000-8000-000000000001/inputs/00000000-0000-4000-8000-000000000002
PUT /api/templates/{id}/inputs/{input} requires template:write. Payload (stripUnknown) is { name (required), driver?, defn?, label?, help?, defaultValue?, required?, multiple? } — note `dataType` is NOT updatable here (it is dropped) and `required`/`multiple` default false when omitted. Returns the updated input, 200.
Request body
{
"name": "report_title",
"label": "Custom Report Title",
"defaultValue": "Q1 Report",
"required": false
}
Response · 200
{
"_links": {
"self": {
"href": "https://informer.example.com/api/templates/00000000-0000-4000-8000-000000000001/inputs/00000000-0000-4000-8000-000000000002"
}
},
"editorComponent": {
"mdMultiInput": {}
},
"tenant": "acme",
"id": "00000000-0000-4000-8000-000000000002",
"driver": "mdMultiInput",
"dataType": "keyword_text",
"defn": null,
"name": "report_title",
"label": "Custom Report Title",
"help": "Enter the title for this report",
"defaultValue": "Q1 Report",
"required": false,
"multiple": false,
"index": 0,
"templateId": "00000000-0000-4000-8000-000000000001",
"createdAt": "2026-01-15T16:20:00.000Z",
"updatedAt": "2026-01-15T16:20:00.000Z"
}
Captured from the API examples test suite; ids and timestamps are normalized.

DELETE /api/templates/{id}/inputs/{input}

Remove an input from a Template.

Authentication: Required

Pre-blocks: template.lookup(params.id), permission.template.edit(pre.template) (derives from template.write)

Response:

Responds 200 with an empty body (Informer convention), not 204. Runs in a database transaction.

Delete an inputDELETE /api/templates/00000000-0000-4000-8000-000000000001/inputs/00000000-0000-4000-8000-000000000002
DELETE /api/templates/{id}/inputs/{input} requires template:edit (which derives from template:write). It is a db.remove inside a transaction and returns 200 with an EMPTY body (Informer convention), NOT 204 as the .md claims.
Response · 200 · no body
Captured from the API examples test suite; ids and timestamps are normalized.

POST /api/templates/{id}/_input-order

Reorder a Template's inputs by writing each input's index.

Authentication: Required

Pre-blocks: template.lookup(params.id), permission.template.write(pre.template) (Designer)

Request Body:

An object with an inputs array, not a bare array of ids. Each item sets the index for one input by id.

FieldTypeRequiredDescription
inputsarrayYesItems of { id, name?, index }
inputs[].idstringYesInput id
inputs[].indexintegerYesNew position
inputs[].namestringNoInput name (optional)

Each item's index is written with the unique-index constraint deferred, so a swap does not trip mid-update.

Example Request:

{
"inputs": [
{ "id": "00000000-0000-4000-8000-000000000002", "index": 0 },
{ "id": "00000000-0000-4000-8000-000000000003", "index": 1 }
]
}

Response:

Responds 200 with an empty body.

Reorder a template’s inputsPOST /api/templates/00000000-0000-4000-8000-000000000001/_input-order
POST /api/templates/{id}/_input-order requires template:write. Payload is an OBJECT { inputs: [{ id, name?, index }] }. Each item’s `index` is written with constraints deferred so the swap does not trip the unique index mid-update. Responds 200 with an empty body (Informer convention) — the update loop returns nothing.
Request body
{
"inputs": [
{
"id": "00000000-0000-4000-8000-000000000002",
"index": 0
},
{
"id": "00000000-0000-4000-8000-000000000003",
"index": 1
}
]
}
Response · 200 · no body
Captured from the API examples test suite; ids and timestamps are normalized.

GET /api/templates/{id}/input-locks

Get a Template's input locks and defaults.

Authentication: Required

Pre-blocks: template.lookup(params.id)

Response:

Returns { settings: { inputLocks, inputDefaults } }, not a flat { locks } map. Locks pin values that users cannot change at render time; defaults pre-fill the inputs but remain editable. Each map is conventionally shaped { params: { ... } }.

Get input locksGET /api/templates/00000000-0000-4000-8000-000000000001/input-locks
GET /api/templates/{id}/input-locks returns { settings: { inputLocks, inputDefaults } } (each conventionally { params: {...} }), NOT a flat { locks: {...} } map. Locks pin values users cannot change; defaults pre-fill them.
Response · 200
{
"_links": {
"self": {
"href": "https://informer.example.com/api/templates/00000000-0000-4000-8000-000000000001/input-locks"
}
},
"settings": {
"inputLocks": {},
"inputDefaults": {}
}
}
Captured from the API examples test suite; ids and timestamps are normalized.

PUT /api/templates/{id}/input-locks

Set a Template's input locks and defaults.

Authentication: Required

Pre-blocks: template.lookup(params.id), permission.template.modifyInputSettings(pre.template) (Member-Plus)

Request Body:

Both inputLocks and inputDefaults objects are required. Each is conventionally shaped { params: { <inputName>: <value> } }.

FieldTypeRequiredDescription
inputLocksobjectYesLocked values, keyed by input name under params
inputDefaultsobjectYesDefault values, keyed by input name under params
Permission differs from the other write routes

This route requires modifyInputSettings (Member-Plus), not template.write.

Example Request:

{
"inputLocks": { "params": { "region": "north" } },
"inputDefaults": { "params": { "report_title": "Monthly Report" } }
}

Response:

The two maps are merged into template.settings (other settings are preserved) and the full updated Template is returned with a Location header, 200.

Set input locksPUT /api/templates/00000000-0000-4000-8000-000000000001/input-locks
PUT /api/templates/{id}/input-locks requires the modifyInputSettings permission (Member-Plus), NOT write. Payload requires BOTH `inputLocks` and `inputDefaults` objects (conventionally { params: {...} }). They are merged into template.settings (other settings preserved) and the full updated TEMPLATE is returned with a Location header, 200.
Request body
{
"inputLocks": {
"params": {
"region": "north"
}
},
"inputDefaults": {
"params": {
"report_title": "Monthly Report"
}
}
}
Response · 200
{
"_links": {
"self": {
"href": "https://informer.example.com/api/templates/admin%3Amonthly-sales-report"
},
"inf:draft": {
"href": "https://informer.example.com/api/templates/admin%3Amonthly-sales-report/draft"
},
"inf:edit": {
"href": "https://informer.example.com/api/templates/admin%3Amonthly-sales-report/_edit"
},
"inf:comments": {
"href": "https://informer.example.com/api/templates/admin%3Amonthly-sales-report/comments{?sort,limit}",
"templated": true
},
"inf:template-share": {
"href": "https://informer.example.com/api/templates/admin%3Amonthly-sales-report/shares/{principalId}",
"templated": true
},
"inf:template-processors": {
"href": "https://informer.example.com/api/templates/admin%3Amonthly-sales-report/processors"
},
"inf:template-inputs": {
"href": "https://informer.example.com/api/templates/admin%3Amonthly-sales-report/inputs"
},
"inf:template-owner": {
"href": "https://informer.example.com/api/templates/admin%3Amonthly-sales-report/owner"
},
"inf:template-shares": {
"href": "https://informer.example.com/api/templates/admin%3Amonthly-sales-report/shares"
},
"inf:template-files": {
"href": "https://informer.example.com/api/templates/admin%3Amonthly-sales-report/files"
},
"inf:template-file-upload": {
"href": "https://informer.example.com/api/templates/admin%3Amonthly-sales-report/files/_upload"
},
"inf:template-file": {
"href": "https://informer.example.com/api/templates/admin%3Amonthly-sales-report/template-file"
},
"inf:export": {
"href": "https://informer.example.com/api/templates/admin%3Amonthly-sales-report/_export"
},
"inf:render": {
"href": "https://informer.example.com/api/templates/admin%3Amonthly-sales-report/_render"
},
"inf:render-prompt": {
"href": "https://informer.example.com/api/templates/admin%3Amonthly-sales-report/_render-prompt"
},
"inf:template-copy": {
"href": "https://informer.example.com/api/templates/admin%3Amonthly-sales-report/_copy"
},
"inf:processor-order": {
"href": "https://informer.example.com/api/templates/admin%3Amonthly-sales-report/_processor-order"
},
"inf:user-settings": {
"href": "https://informer.example.com/api/templates/admin%3Amonthly-sales-report/settings"
},
"inf:template-datasets": {
"href": "https://informer.example.com/api/templates/admin%3Amonthly-sales-report/datasets"
},
"inf:input-order": {
"href": "https://informer.example.com/api/templates/admin%3Amonthly-sales-report/_input-order"
},
"inf:favorite": {
"href": "https://informer.example.com/api/templates/admin%3Amonthly-sales-report/favorite"
},
"inf:template-input-locks": {
"href": "https://informer.example.com/api/templates/admin%3Amonthly-sales-report/input-locks"
}
},
"naturalId": "admin:monthly-sales-report",
"permissions": {
"assignTags": true,
"edit": true,
"share": true,
"delete": true,
"write": true,
"changeOwner": true,
"copy": true,
"rename": true,
"modifyInputSettings": true
},
"tenant": "acme",
"id": "00000000-0000-4000-8000-000000000001",
"type": "nunjucks",
"name": "Monthly Sales Report",
"ownerId": "admin",
"slug": "monthly-sales-report",
"description": null,
"settings": {
"inputLocks": {
"params": {
"region": "north"
}
},
"inputDefaults": {
"params": {
"report_title": "Monthly Report"
}
}
},
"editingId": null,
"templateFileId": null,
"letterheadId": null,
"handler": "pdf",
"handlerOptions": {},
"source": null,
"sourceId": null,
"shared": false,
"createdAt": "2026-01-15T16:20:00.000Z",
"updatedAt": "2026-01-15T16:20:00.000Z",
"folderId": null,
"editing": null,
"inputs": [
{
"editorComponent": {
"mdMultiInput": {}
},
"tenant": "acme",
"id": "00000000-0000-4000-8000-000000000002",
"driver": "mdMultiInput",
"dataType": "keyword_text",
"defn": null,
"name": "customer_name",
"label": "Customer Name",
"help": "Optional customer filter",
"defaultValue": null,
"required": false,
"multiple": false,
"index": 3,
"templateId": "00000000-0000-4000-8000-000000000001",
"createdAt": "2026-01-15T16:20:00.000Z",
"updatedAt": "2026-01-15T16:20:00.000Z"
},
{
"editorComponent": {
"mdSelect": {}
},
"tenant": "acme",
"id": "00000000-0000-4000-8000-000000000003",
"driver": "mdSelect",
"dataType": "keyword_text",
"defn": null,
"name": "region",
"label": "Region",
"help": null,
"defaultValue": null,
"required": false,
"multiple": false,
"index": 0,
"templateId": "00000000-0000-4000-8000-000000000001",
"createdAt": "2026-01-15T16:20:00.000Z",
"updatedAt": "2026-01-15T16:20:00.000Z"
},
{
"editorComponent": {
"mdMultiInput": {}
},
"tenant": "acme",
"id": "00000000-0000-4000-8000-000000000004",
"driver": "mdMultiInput",
"dataType": "keyword_text",
"defn": null,
"name": "report_title",
"label": "Custom Report Title",
"help": "Enter the title for this report",
"defaultValue": "Q1 Report",
"required": false,
"multiple": false,
"index": 1,
"templateId": "00000000-0000-4000-8000-000000000001",
"createdAt": "2026-01-15T16:20:00.000Z",
"updatedAt": "2026-01-15T16:20:00.000Z"
}
],
"processors": [],
"files": []
}
Captured from the API examples test suite; ids and timestamps are normalized.