Skip to content

Decision table

The decision table (Tabla de decisión) is a Zoan Flow step that decides with rules you write, like a decision matrix in a spreadsheet: each row says “if the case data looks like this, the result is that”. What you get:

  • The same result every time. Two cases with the same data take the same row, today and a year from now.
  • No cost per case. It uses no AI models and consumes no AI budget.
  • Instant. The step resolves as soon as the case arrives; it does not wait for a robot or a person.
  • Decisions you can explain. The monitor shows which row decided and with which values, ready for an audit.
  • Rules the business can maintain. Rows are written as in Excel (and can be pasted from Excel or imported as CSV): changing a threshold or a role does not require touching the robot.
Use a decision table when…Use an AI step when…
The rule already exists and can be written down (thresholds, ranges, value lists, dates).Free text (the description of a request, or the text a robot pulled from an email or a document) has to be read to get the data out.
You need the same result every time and to show why.Something has to be classified or drafted that does not fit a closed list of rules.
Volume is high and you want every case to cost the same: nothing.The value lies in understanding the content, not in applying a policy.

The two work together: the AI leaves the data in case variables and the table decides with them.

In the canvas palette, group Decisiones (decisions), drag Tabla de decisión onto the diagram. When you select it, the properties panel shows:

  • Reglas (rules): a summary of the table and the Editar y probar la tabla (edit and test the table) button, which opens the editor (in a flow you can only view, the button is Ver y probar la tabla, view and test the table).
  • Enrutar por lo que decide (route by what it decides, optional): creates the gateway that splits cases by an output. See Combining it with a gateway.

The table has four parts: inputs, outputs, rows and the fixed En cualquier otro caso (any other case) row.

Inputs are the case variables the decision is based on; each one is a column of conditions. With Agregar entrada (add input) you pick a flow variable. Each input has a type: Número (number), Entero (integer), Texto (text), Fecha (date), Fecha y hora (date and time), Sí/No (yes/no) or Lista (list). If the data is inside an object (for example, respuesta.fields.monto), choose Un campo dentro de un objeto… (a field inside an object), type the path and declare its type, because the object does not carry it.

Outputs are the variables the step writes when it decides; each one is a column of results. With Agregar salida (add output) you pick an existing variable or Una variable nueva… (a new variable), which gets declared in the flow. In each row, an output can hold:

  • a fixed value (Jefe de área, 2, sí);
  • vacío (empty), to leave the variable without a value;
  • a copy of another variable with {{variable}}, for example {{correo_solicitante}}.

Each row is a rule: one condition per input and one value per output. A row holds when all its conditions hold.

  • Agregar fila (add row) adds one at the end. Each row has buttons to move up, move down, duplicate and delete it.
  • The Descripción (description) column is optional: a short name for the rule (for example, Monto medio) that you will later see in the tester and in the monitor.
  • Paste from Excel. Copy a block of cells in Excel and paste it into a table cell: the rows and columns it needs are filled in, and if the block includes the header, it is skipped automatically.
  • Below the table you see how the active cell was understood, or its error, as you type.

The last row, En cualquier otro caso (any other case), is fixed and required: it says what happens when no row holds. You have two options:

  • Dar estos valores (give these values) to the outputs, like one more row that always holds; or
  • Abrir un incidente para que una persona decida (open an incident for a person to decide). Use this when a case that fits no rule is a sign of badly captured data.
ButtonWhat for
Probar (test)Opens the tester. See Testing the table.
CSVDownloads the table as CSV, separated by ;, to review or edit it in Excel.
Importar (import)Replaces the rows with those of a CSV. See Importing from CSV.
Listo (done)Closes the editor and returns to the canvas.

Cells are written as in a spreadsheet. The keywords are in Spanish, as in the product.

You writeIt holds when the value…
-is anything (the input does not matter in this row)
5 or = 5equals 5
≠ 5 or != 5is not 5
< 5, ≤ 5 (or <= 5)is less than, or less than or equal
> 5, ≥ 5 (or >= 5)is greater than, or greater than or equal
[5 .. 50]is between 5 and 50, both ends included
(5 .. 50)is between 5 and 50, ends excluded
[5 .. 50)is between 5 and 50, 5 included and 50 excluded (brackets and parentheses can be mixed)
entre 5 y 50same as [5 .. 50]
a; b; cis one of the list values (for text, also comma-separated: a, b, c)
contiene xcontains the text x
vacíohas no value
con valorhas a value

Formats:

  • Numbers with a dot as thousands separator and a comma for decimals: 12.500.000 or 12500000; 0,5.
  • Dates as AAAA-MM-DD (YYYY-MM-DD) or DD/MM/AAAA (DD/MM/YYYY): 2026-10-31 or 31/10/2026.
  • Literal text in quotes. To compare with text that matches the syntax, put it in quotes: "-" is the dash, not “anything”; "vacío" is the word, not “no value”.
You writeThe output gets
Jefe de área, 2, 2026-12-31that value
sí / notrue or false
vacíono value
{{variable}}the value of another case variable

In the Si varias filas se cumplen (if several rows hold) selector you choose how a table where more than one row could hold is resolved:

  • Gana la primera (el orden importa) (the first one wins, order matters), the default. Rows are checked top to bottom and the first one that holds wins. Put specific cases at the top and general ones at the bottom.
  • Solo puede cumplirse una (única) (only one can hold, unique). When publishing, the review flags overlapping rows; if several still hold for a case, the case becomes an incident instead of picking one at random. Use it when you want the guarantee that rules do not overlap.

The editor’s Revisión (review) section lists the table’s problems and, when you click one, takes you to the cell. The same review runs when you Validar (validate) or Publicar (publish) the flow.

CheckExample
Incompatible types> 5.000.000 in a Texto input.
Variables that do not existAn input or a {{variable}} not declared in the flow.
Outputs without a valueA blank output cell (to leave it without a value, write vacío).
Empty ranges[50 .. 5], which contains no value.
Rows that never decideWith Gana la primera, a row fully covered by the ones above.
Overlapping rowsWith Solo puede cumplirse una, two rows the same case can meet.
Gaps between rangesWhen the table decides on a single number or date input, a value no row covers; for example, rows < 5.000.000 and (5.000.000 .. 50.000.000] leave 5.000.000 uncovered.
Copied sensitive dataAn output that copies a variable marked as sensitive with {{variable}}.

Errors block publishing; warnings can be accepted.

  • Tester. In the editor click Probar and type values for the inputs, or use Probar con valores de ejemplo (test with sample values). You immediately see the deciding row, for example “Decide la fila 2 (Monto medio).” (row 2 decides), and what it writes to each output. It is the quickest way to check the edges of each range.
  • Flow simulation. When you Simular (simulate) the whole flow, the table is evaluated with the sample data and the path shows decidió fila N (row N decided) on the step, and where the case went next.

Importar replaces all the table rows with those of a CSV file separated by ;, like the one CSV downloads:

  • The first row is the header with the column names. The first column is Descripción; outputs have → before their name (for example, → rol_aprobador).
  • Each following row is a rule, written with the same cell syntax.
  • A row whose description is En cualquier otro caso gives the values of that fixed row. If you write incidente in one of its outputs, it becomes Abrir un incidente para que una persona decida.
Descripción;monto;tipo_gasto;→ aprobacion_automatica;→ rol_aprobador;→ nivel
Monto bajo;< 5.000.000;-;sí;vacío;0
Monto medio;[5.000.000 .. 50.000.000];viajes, papelería;no;Jefe de área;1
Monto medio, otros gastos;[5.000.000 .. 50.000.000];-;no;Director financiero;1
En cualquier otro caso;;;no;Gerencia general;2

Since ; separates the file’s columns, in a hand-written CSV text lists are separated by commas.

The table calculates; the exclusive gateway routes. After the table, the gateway compares a table output, for example aprobacion_automatica is true.

To avoid building routes by hand, use the Enrutar por lo que decide section of the step panel: pick the output and click Crear compuerta (create gateway). The canvas adds an exclusive gateway with:

  • one branch per fixed value the output takes in the table, with its condition already written and ending in a temporary end that you later connect to the right step;
  • an En otro caso (otherwise) branch to the step the table used to lead to.

If rol_aprobador can be Jefe de área, Director financiero or Gerencia general, you get three branches plus En otro caso. If it is not what you wanted, Ctrl+Z undoes it.

In the step detail, the monitor shows which row decided and with which values:

Fila 2 · Monto medio
Monto: 12.500.000 — entre 5.000.000 y 50.000.000
→ Rol aprobador = Jefe de área

The case log gets a line like Tabla «Nivel de aprobación»: decidió la fila 2 (…) (table decided row 2). So anyone can tell why the case took that path without opening the flow definition. Values of variables marked as sensitive are shown masked.

IncidentWhat happened
Ninguna fila de la tabla se cumple (no table row holds)The case fit no row and En cualquier otro caso is set to open an incident.
Varias filas de la tabla se cumplen (several table rows hold)With Solo puede cumplirse una, the case meets more than one row.
La tabla copia un dato que no tiene valor (the table copies data with no value)An output uses {{variable}} and that variable has no value in this case.

You have two ways to resolve it:

  • Corregir y reintentar (fix and retry). Fix the case variables that were wrong and retry: the table is evaluated again with the corrected data.
  • Skip the step giving the outputs. If the case is an exception, skip the step and type the output values yourself, so the following steps have what they need.

If the incident comes from the table itself (two overlapping rows, a gap between ranges), fix it, publish a new version and handle the stopped cases.

A fictitious company approves expense requests based on the amount and the expense type. The data in this example is made up.

Flow variables: monto (Número) and tipo_gasto (Texto) as inputs; aprobacion_automatica (Sí/No), rol_aprobador (Texto) and nivel (Entero) as outputs.

Table “Nivel de aprobación”, with Gana la primera (el orden importa):

#Descripciónmontotipo_gastoaprobacion_automaticarol_aprobadornivel
1Monto bajo< 5.000.000-sívacío0
2Monto medio[5.000.000 .. 50.000.000]viajes; papeleríanoJefe de área1
3Monto medio, otros gastos[5.000.000 .. 50.000.000]-noDirector financiero1
En cualquier otro casonoGerencia general2

Row 2 comes before row 3 because it is the specific case: a travel request for 12.500.000 meets both, and the first one wins. With 80.000.000, no row holds and En cualquier otro caso decides.

After the table, an exclusive gateway ¿Aprobación automática? splits the paths: if aprobacion_automatica is true, the case goes straight on; otherwise it reaches a Persona (person) task assigned to the role {{rol_aprobador}}.

flowchart LR
  A([Start]) --> T[Decision table<br/>Nivel de aprobación]
  T --> G{¿Aprobación<br/>automática?}
  G -- Yes --> R[Register the request]
  G -- No --> P[Person<br/>role: rol_aprobador]
  P --> R
  R --> F([End])

The rule lives in one place: if tomorrow the threshold goes up to 8.000.000 or travel moves to another role, you change one cell and publish.

When you download the flow as JSON, the table looks like this:

{
"type": "decisionTable",
"name": "Nivel de aprobación",
"decision": {
"hitPolicy": "first",
"inputs": [ { "var": "monto" }, { "var": "tipo_gasto" } ],
"outputs": [ { "var": "aprobacion_automatica" }, { "var": "rol_aprobador" }, { "var": "nivel" } ],
"rules": [
{
"id": "bajo",
"when": { "monto": { "op": "lt", "value": 5000000 } },
"then": { "aprobacion_automatica": { "value": true }, "rol_aprobador": { "value": null }, "nivel": { "value": 0 } }
},
{
"id": "viajes",
"when": {
"monto": { "op": "between", "min": 5000000, "max": 50000000 },
"tipo_gasto": { "op": "in", "values": ["viajes", "papelería"] }
},
"then": { "aprobacion_automatica": { "value": false }, "rol_aprobador": { "value": "Jefe de área" }, "nivel": { "value": 1 } }
},
{
"id": "medio",
"when": { "monto": { "op": "between", "min": 5000000, "max": 50000000 } },
"then": { "aprobacion_automatica": { "value": false }, "rol_aprobador": { "value": "Director financiero" }, "nivel": { "value": 1 } }
}
],
"otherwise": {
"then": { "aprobacion_automatica": { "value": false }, "rol_aprobador": { "value": "Gerencia general" }, "nivel": { "value": 2 } }
}
}
}

hitPolicy is first for Gana la primera (el orden importa) and unique for Solo puede cumplirse una (única). To have a case with no matching row open an incident, otherwise is { "incident": true }.

when operators:

opSame asFields
any-—
eq, neq=, ≠value
lt, lte, gt, gte<, ≤, >, ≥value
between[a .. b]min, max; minExclusive and maxExclusive (true) to exclude an end
ina; b; cvalues
containscontiene xvalue
empty, notEmptyvacío, con valor—
ItemMaximum per table
Rows500
Inputs20
Outputs20
Values in a list (a; b; c)200