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.
Schema 2
Section titled “Schema 2”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" } } }}| Property | What it is |
|---|---|
schema | Always 2. |
title, description | Optional. |
inputs | The inputs: key → declaration (see below). |
sections | List of sections: id, title (optional), description, columns (1 to 4), collapsible, visibleWhen and items ({ "field": "<key>", "span": 1-4 }). |
fields | The fields: key → definition. The key matches ^[a-z][a-z0-9_]{0,59}$ and is unique across the whole form. |
Inputs
Section titled “Inputs”type (string, number, integer, boolean, date, datetime, object, array or file), required, label, description, sensitive, mask ("last4") and, for lists, items, minItems and maxItems.
Fields
Section titled “Fields”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.
| Group | Properties |
|---|---|
| Common | label, 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) |
| Text | minLength, 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 date | min, max (for dates, "YYYY-MM-DD" or { "today": n }), minRef, maxRef, decimals (0 to 6), step, currency (COP, USD, EUR) |
| Selection | options (strings or { "value", "label" }), optionsFrom (an input of type list), minItems, maxItems, requiredTrue (checkbox) |
| Phone | defaultCountry |
| File | accept (pdf, imagen, office, csv, texto), maxFiles, maxSizeMb |
| Table | columns (key → column definition), columnOrder, minRows, maxRows |
| Display | text (in info), value and format (in display) |
Conditions
Section titled “Conditions”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,emptyandtruthy. - Paths:
campos.<key>(fields),entradas.<key>(inputs),fila.<column>(row: inside a table, only columns of the same row) anddecision(only inrequiredWhen).
A hidden field counts as empty. A comparison between types that don’t match is not met.
Calculations
Section titled “Calculations”| Expression | Result |
|---|---|
{ "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.
Value format
Section titled “Value format”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 astrueorfalse.
{ "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.
Limits
Section titled “Limits”| What | Limit |
|---|---|
| Definition | 128 KB; 150 fields (counting columns); 30 sections; 4 columns per section |
| Tables | 10 per form; 20 columns; 200 rows |
| Options | 200 per field, up to 200 characters each; single choice, up to 8 |
| Rules and calculations | 400 comparisons and 4 levels; one calculation, 60 nodes and 8 levels |
| Text | Up to 10,000 characters (500 by default for text and 4,000 for text area); email 254; comment 4,000 |
| Inputs | 50; 64 KB per value and 256 KB in total |
| Answer | 128 KB |
| Files | 10 per field, up to 25 MB each; 30 files and 100 MB per task |
| Other | Key 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.
Library
Section titled “Library”| Call | Permission | What it is for |
|---|---|---|
GET /forms?status=active|archived|all&search=&limit=&offset= | form:list | List. |
POST /forms | form:manage | Create: { "name", "description", "draft" }. |
GET /forms/:id | form:list | Read, with its ETag. |
PUT /forms/:id/draft | form:manage | Save the draft: { "draft", "revision" }, with the If-Match header (without it, 428). |
PATCH /forms/:id | form:manage | Change the name or description. |
POST /forms/:id/publish | form:manage | Publish: { "revision", "notes", "confirmIncompatible" }. |
POST /forms/:id/duplicate | form:manage | Duplicate. |
POST /forms/:id/archive, POST /forms/:id/unarchive | form:manage | Archive and restore. |
GET /forms/:id/versions, GET /forms/:id/versions/:v | form:list | Published versions. |
GET /forms/:id/export?version= | form:list | Export to JSON (to import into another environment). |
GET /forms/:id/usage | form:list | Where it is used. |
GET /forms/catalog | form:list | The published forms, to pick from. |
POST /forms/validate | form: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/check | same 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.
Tasks with a form
Section titled “Tasks with a form”- Create:
POST /human-tasks(permissionhuman_task:manage) withform_ref: { "name" | "id", "version" }andform_inputs, or with your own form inform: { "schema": 2, … }andform_inputs. - Answer attachments:
POST /human-tasks/:id/uploads(multipart, withfield) andDELETE /human-tasks/:id/uploads/:uploadId, with permission to decide the task.GET /human-tasks/:id/files/:fileId/urlreturns a link to open a file. - Answer:
POST /human-tasks/:id/complete, as always, with the fields infields. If the answer does not pass the rules:422withFORM_INVALID, the message «Revisa los campos marcados.» (Check the highlighted fields.) and one error per field (field, plusrowandcolumnin a table). - Sensitive data:
POST /human-tasks/:id/sensitive/revealreturns 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.
Errors
Section titled “Errors”| Code | When |
|---|---|
400 FORM_NOT_FOUND, FORM_ARCHIVED, FORM_VERSION_NOT_FOUND | The form or the requested version does not exist or is archived. |
403 PLAN_FEATURE_MISSING | Your plan does not include form design. |
403 SENSITIVE_FORBIDDEN | You cannot see that task’s sensitive data. |
404 FORMS_DISABLED | Forms are not available in your Zoan Cloud yet. |
409 DRAFT_REVISION_CONFLICT | Someone else saved the draft first. |
409 NO_CHANGES | There are no changes to publish. |
409 FORM_INCOMPATIBLE | The change is incompatible and was not confirmed. |
413 DRAFT_TOO_LARGE, PAYLOAD_TOO_LARGE | The draft or the request exceeds the allowed size. |
422 FORM_INVALID | The definition or the answer is not valid; the response includes the details. |
429 max_forms | The plan’s limit of active forms was reached. |
503 FORMS_TASKS_DISABLED | Tasks with a form are not available in your Zoan Cloud yet. |