Skip to content

Schema and API reference

This page is for anyone who wants to read or write a form in JSON (with Editar como JSON (Edit as JSON), Importar (Import) or the API) or integrate it from their own application. To design with the mouse, see Design a form.

A form is a JSON document with schema: 2. Technical keys are in English; the text a person sees is in your language.

{
"schema": 2,
"title": "Ítems con total",
"description": "Opcional",
"inputs": {
"solicitante": { "type": "string", "required": true, "label": "Solicitante" }
},
"sections": [
{ "id": "s_items", "title": "Ítems", "columns": 1,
"items": [ { "field": "items" }, { "field": "total" }, { "field": "motivo_rechazo" } ] }
],
"fields": {
"items": {
"type": "table", "label": "Ítems", "minRows": 1, "maxRows": 50,
"columns": {
"concepto": { "type": "text", "label": "Concepto", "required": true },
"cantidad": { "type": "number", "label": "Cantidad", "required": true, "min": 1, "decimals": 0 },
"valor": { "type": "currency", "label": "Valor unitario", "currency": "COP", "required": true },
"subtotal": { "type": "currency", "label": "Subtotal", "currency": "COP",
"calc": { "op": "mul", "args": [ { "ref": "fila.cantidad" }, { "ref": "fila.valor" } ] } }
},
"columnOrder": ["concepto", "cantidad", "valor", "subtotal"]
},
"total": { "type": "currency", "label": "Total", "currency": "COP",
"calc": { "op": "sum", "table": "items", "column": "subtotal" } },
"motivo_rechazo": { "type": "textarea", "label": "Motivo del rechazo",
"requiredWhen": { "var": "decision", "op": "eq", "value": "Rechazar" } }
}
}
PropertyWhat it is
schemaAlways 2.
title, descriptionOptional.
inputsThe inputs: key → declaration (see below).
sectionsList of sections: id, title (optional), description, columns (1 to 4), collapsible, visibleWhen and items ({ "field": "<key>", "span": 1-4 }).
fieldsThe fields: key → definition. The key matches ^[a-z][a-z0-9_]{0,59}$ and is unique across the whole form.

type (string, number, integer, boolean, date, datetime, object, array or file), required, label, description, sensitive, mask ("last4") and, for lists, items, minItems and maxItems.

type is one of: text, textarea, number, currency, email, phone, select, multiselect, radio, checkbox, date, datetime, file, table, info and display. Each type accepts only its own properties; a property the type does not support is an error.

GroupProperties
Commonlabel, help, placeholder, required, requiredWhen, visibleWhen, readOnly, readOnlyWhen, calc, default (a value or {{entradas.<key>}}), sensitive, mask, messages (custom messages per validation, up to 300 characters)
TextminLength, maxLength, pattern ({ "name": "cedula_co" | "nit_co" | "placa_co" | "solo_digitos" | "alfanumerico" } or { "custom": "<regular expression>" }, which must match the whole value), rows
Number, currency and datemin, max (for dates, "YYYY-MM-DD" or { "today": n }), minRef, maxRef, decimals (0 to 6), step, currency (COP, USD, EUR)
Selectionoptions (strings or { "value", "label" }), optionsFrom (an input of type list), minItems, maxItems, requiredTrue (checkbox)
PhonedefaultCountry
Fileaccept (pdf, imagen, office, csv, texto), maxFiles, maxSizeMb
Tablecolumns (key → column definition), columnOrder, minRows, maxRows
Displaytext (in info), value and format (in display)

Rules (visibleWhen, requiredWhen, readOnlyWhen and a calculation’s when) use conditions:

  • Leaf: { "var": "<path>", "op": "<comparison>", "value": … } or, to compare with another field, { "var": "<path>", "op": "<comparison>", "ref": "<other path>" }.
  • Groups: { "all": [ … ] } (all), { "any": [ … ] } (any one) and { "not": … } (not met). Up to 4 levels.
  • Comparisons: eq, neq, gt, gte, lt, lte, contains, in, exists, empty and truthy.
  • Paths: campos.<key> (fields), entradas.<key> (inputs), fila.<column> (row: inside a table, only columns of the same row) and decision (only in requiredWhen).

A hidden field counts as empty. A comparison between types that don’t match is not met.

ExpressionResult
{ "const": 100 }, { "ref": "campos.valor" }A fixed value or another field’s value.
{ "op": "add" | "sub" | "mul" | "min" | "max", "args": [ … ] }Addition, subtraction, multiplication, minimum or maximum (2 to 10 arguments).
{ "op": "div", "args": [a, b], "decimals": 2 }Division; dividing by 0 leaves it empty.
{ "op": "round", "arg": …, "decimals": 0 }Rounding (halves round away from zero).
{ "op": "sum", "table": "items", "column": "subtotal" }Sum of a column (empty and hidden values count as 0).
{ "op": "count", "table": "items" }Number of rows.
{ "op": "if", "when": <condition>, "then": …, "else": … }One value or the other depending on a condition.
{ "op": "concat", "args": [ … ] }Joins text.
{ "op": "days", "from": …, "to": … }Days between two dates.
{ "op": "coalesce", "args": [ … ] }The first value that is not empty.

The math uses exact decimals. An empty operand leaves the result empty, except in sum, count, concat and coalesce. A calculated field is always read only, and Zoan Cloud recalculates it when it receives the answer.

Currency and numbers as a JSON number; date as "YYYY-MM-DD"; date and time as ISO with time zone; phone in international format (+573001234567); file as [{ "id": "…" }], which Zoan Cloud completes with bucket, name, content_type and size. A task’s answer has the shape { "decision", "comment", "fields" }.

fields always carries the same keys, so a robot or a flow step can read them without first checking whether they exist:

  • Every form field appears in fields (except informative text and read-only value fields, which are only displayed).
  • Every table row carries all of its columns, in the form’s order.
  • Anything without a value arrives as null, whatever its type: an optional field left blank, an empty cell, a field hidden by a rule or one that does not apply to the chosen decision. A visible checkbox arrives as true or false.
{ "decision": "Aprobar", "comment": "ok",
"fields": { "nota": null,
"productos": [ { "sku": "P-1", "rotacion_mensual": 12 },
{ "sku": "P-2", "rotacion_mensual": null } ] } }

In a start form, a field that is null is not passed to the case: the variable keeps its default value.

WhatLimit
Definition128 KB; 150 fields (counting columns); 30 sections; 4 columns per section
Tables10 per form; 20 columns; 200 rows
Options200 per field, up to 200 characters each; single choice, up to 8
Rules and calculations400 comparisons and 4 levels; one calculation, 60 nodes and 8 levels
TextUp to 10,000 characters (500 by default for text and 4,000 for text area); email 254; comment 4,000
Inputs50; 64 KB per value and 256 KB in total
Answer128 KB
Files10 per field, up to 25 MB each; 30 files and 100 MB per task
OtherKey 60 characters; label 200; help 1,000; information text 5,000; custom pattern 200; relative dates ±3,650 days

Calls are made with a person’s session (Authorization: Bearer <token-de-sesion>) and under the environment: https://<tu-api>/environments/<ambiente>/…. The body can be up to 600 KB.

CallPermissionWhat it is for
GET /forms?status=active|archived|all&search=&limit=&offset=form:listList.
POST /formsform:manageCreate: { "name", "description", "draft" }.
GET /forms/:idform:listRead, with its ETag.
PUT /forms/:id/draftform:manageSave the draft: { "draft", "revision" }, with the If-Match header (without it, 428).
PATCH /forms/:idform:manageChange the name or description.
POST /forms/:id/publishform:managePublish: { "revision", "notes", "confirmIncompatible" }.
POST /forms/:id/duplicateform:manageDuplicate.
POST /forms/:id/archive, POST /forms/:id/unarchiveform:manageArchive and restore.
GET /forms/:id/versions, GET /forms/:id/versions/:vform:listPublished versions.
GET /forms/:id/export?version=form:listExport to JSON (to import into another environment).
GET /forms/:id/usageform:listWhere it is used.
GET /forms/catalogform:listThe published forms, to pick from.
POST /forms/validateform:list (or designing flows or assistants)Check a definition: errors, warnings, the decisions it uses, sensitive fields and the shape of the answer.
POST /forms/checksame as validate«Probar envío» (Test submission): { "definition", "decision", "comment", "inputs", "fields" }. Returns { ok, stage, outcome | errors, clock } without creating anything.

To publish an incompatible change, confirmIncompatible must be the number of the current version; without it, the response is 409 FORM_INCOMPATIBLE with the changes, the robots that use the form and who has another version pinned.

  • Create: POST /human-tasks (permission human_task:manage) with form_ref: { "name" | "id", "version" } and form_inputs, or with your own form in form: { "schema": 2, … } and form_inputs.
  • Answer attachments: POST /human-tasks/:id/uploads (multipart, with field) and DELETE /human-tasks/:id/uploads/:uploadId, with permission to decide the task. GET /human-tasks/:id/files/:fileId/url returns a link to open a file.
  • Answer: POST /human-tasks/:id/complete, as always, with the fields in fields. If the answer does not pass the rules: 422 with FORM_INVALID, the message «Revisa los campos marcados.» (Check the highlighted fields.) and one error per field (field, plus row and column in a table).
  • Sensitive data: POST /human-tasks/:id/sensitive/reveal returns the values in clear text to those allowed to see them, and is recorded in the audit log.

In the task list and in live notifications, form answers do not include field values (fields_omitted: true): read them from the task detail.

CodeWhen
400 FORM_NOT_FOUND, FORM_ARCHIVED, FORM_VERSION_NOT_FOUNDThe form or the requested version does not exist or is archived.
403 PLAN_FEATURE_MISSINGYour plan does not include form design.
403 SENSITIVE_FORBIDDENYou cannot see that task’s sensitive data.
404 FORMS_DISABLEDForms are not available in your Zoan Cloud yet.
409 DRAFT_REVISION_CONFLICTSomeone else saved the draft first.
409 NO_CHANGESThere are no changes to publish.
409 FORM_INCOMPATIBLEThe change is incompatible and was not confirmed.
413 DRAFT_TOO_LARGE, PAYLOAD_TOO_LARGEThe draft or the request exceeds the allowed size.
422 FORM_INVALIDThe definition or the answer is not valid; the response includes the details.
429 max_formsThe plan’s limit of active forms was reached.
503 FORMS_TASKS_DISABLEDTasks with a form are not available in your Zoan Cloud yet.