Decision tables: keep your rules in a table, run them as an API

A decision table is a table that lists the conditions of a decision and, for every combination of them, the outcome. Each rule is one row, so nested if-then logic becomes something anyone can read, check and change.

What a decision table is

A decision table has three parts. Conditions are the questions the decision depends on: in the example, the customer tier and the order value. Rules are the combinations of answers that matter, one per row. Outcomes (also called actions or conclusions) say what happens when a rule applies: here the discount, and whether someone has to approve it.

Read it from top to bottom: the first rule whose conditions all hold decides. A Gold customer who orders 12,000 gets 15 % and needs approval (rule 1). Rule 8 is the default: it catches every case no other rule covers, so the table always gives an answer.

Some authors write each rule as a column and the conditions as rows; the content is the same. In a spreadsheet, rules as rows are easier to maintain, because a new rule is a new row.

A discount decision: two conditions, two outcomes, eight rules
RuleCustomer tierOrder valueDiscountApproval
1Goldfrom 10,00015%yes
2Goldfrom 2,50010%no
3Goldany5%no
4Silverfrom 10,00010%yes
5Silverfrom 2,5006%no
6Silverany3%no
7Standardfrom 10,0003%no
8any (default)any0%no

Why teams use decision tables.

Rows instead of nested IFs

The same discount logic as code is an if inside an if inside an if. In a table every rule stands on its own line, so the person who owns the discount policy can read it and a reviewer can check it rule by rule.

Complete, without contradictions

A table makes gaps visible: three tiers and three order bands give nine combinations, and you can tick off that each one lands on a rule. Two rules with different outcomes for the same case stand next to each other.

Maintained by the people who know the rules

Sales moves a threshold, finance changes a rate. The change is an edit to a cell, made by the specialist, and the software around the table stays as it is.

How to build a decision table in a spreadsheet.

A spreadsheet evaluates a decision table with lookup formulas. Which one fits depends on the shape of the table: a grid for one or two conditions, a rule list when rules overlap or leave a condition open.

Shape 1: a grid

One condition down, one across. B1 holds the customer tier, B2 the order value.

HIJK
8Order value fromGoldSilverStandard
905%3%0%
102,50010%6%0%
1110,00015%10%3%

Shape 2: a rule list

inputsresults

One rule per row; column F says whether the row matches the inputs in B1 and B2.

AB
1Customer tierGold
2Order value12,000
3
4Discount15%
5Approval neededTRUE
6Rule applied1
ABCDEF
8RuleCustomer tierOrder value fromDiscountApproval neededMatches
91Gold10,00015%TRUETRUE
102Gold2,50010%FALSETRUE
113Gold05%FALSETRUE
124Silver10,00010%TRUEFALSE
135Silver2,5006%FALSEFALSE
146Silver03%FALSEFALSE
157Standard10,0003%FALSEFALSE
16800%FALSETRUE
One condition with ranges

A lookup with thresholds

With a single condition, the table is a list of thresholds and outcomes, such as the Gold column of the grid. XLOOKUP with match mode -1 returns the entry of the largest threshold that is not above the order value.

=XLOOKUP(B2,H9:H11,I9:I11,,-1)

The same with INDEX and MATCH. Here the thresholds have to be sorted in ascending order.

=INDEX(I9:I11,MATCH(B2,H9:H11,1))

For an order value of 12,000 both return 15 %.

Two conditions

A grid with two lookups

With two conditions, put one on the rows and the other on the columns. INDEX takes the row from one MATCH and the column from a second. IFERROR supplies the default: a tier that is not in the header row gets 0.

=IFERROR(INDEX(I9:K11,MATCH(B2,H9:H11,1),MATCH(B1,I8:K8,0)),0)

Gold and 12,000 return 15 %; a tier the grid does not know returns 0 %.

First matching rule

A rule list with a helper column

A grid stops working when rules overlap, leave a condition open or depend on more than two conditions. Then write one rule per row and let a helper column say whether the row matches; an empty condition cell means "any". This is F9, copied down to F16:

=AND(OR(B9="",B9=$B$1),$B$2>=C9)

The results in B4, B5 and B6 take the first row whose helper cell is TRUE:

=XLOOKUP(TRUE,F9:F16,D9:D16)
=XLOOKUP(TRUE,F9:F16,E9:E16)
=XLOOKUP(TRUE,F9:F16,A9:A16)

Without a helper column, FILTER keeps the matching rows and INDEX takes the first:

=INDEX(FILTER(D9:D16,((B9:B16="")+(B9:B16=B1))*(B2>=C9:C16)),1)

Gold and 12,000 match rules 1, 2, 3 and 8. The first one decides: 15 %, approval needed, rule 1.

The default row

A last rule that always matches

Rule 8 has no tier and starts at 0, so it matches every case. Without it, a case no rule covers ends in #N/A, and a caller of the API receives that error text as the result. Keep the default row last and give it a safe outcome.

A Standard customer who orders 9,999 lands on rule 8: 0 %, no approval.

These are the formulas of the live example below; the demo workbook contains exactly these cells.

From decision table to API.

A decision table in a file answers one person at a time. Published as an API, it answers your shop, your CRM and your workflows with the same rules.

Step 1

Mark inputs and results

The condition cells become inputs (B1 as tier, B2 as order_value), the outcome cells become results (B4, B5, B6). An input can be limited to a range or to allowed values; a call outside them is refused before anything is calculated.

Step 2

Test and publish

Try known cases in the test panel, then publish. To change a rule, edit the table and publish again; until then callers keep getting the published version.

Step 3

Call it

One request with the condition values returns the outcomes as JSON. Callers see inputs and results, never the formulas.

The request and the answer

GET https://spreadapi.io/api/v1/services/{serviceId}/execute?tier=Gold&order_value=12000
{
  "inputs": [
    { "name": "tier", "title": "Customer tier", "value": "Gold" },
    { "name": "order_value", "title": "Order value", "value": 12000 }
  ],
  "outputs": [
    { "name": "discount", "title": "Discount",
      "value": 0.15, "formatString": "0%" },
    { "name": "approval_needed", "title": "Approval needed",
      "value": true },
    { "name": "rule", "title": "Rule applied", "value": 1 }
  ]
}

Shortened: the answer also carries the parts service and metadata. A percentage comes back as a decimal, 0.15 for 15 %.

Discount decisionlive example · invented rules
Example values of the demo workbook
Discount15%
Approval needed
yes
Rule applied
1: Gold, from 10,000
See the call

An example, not a live call: the request for the default values and the outputs of the answer.

GETRequest
https://spreadapi.io/api/v1/services/410c7f87-78c7-42db-83dd-edbe83cb2d03_mwfd9hcw69zkq/execute?tier=Gold&order_value=12000
Answer
{
  "outputs": [
    {
      "name": "discount",
      "title": "Discount",
      "value": 0.15,
      "formatString": "0%"
    },
    {
      "name": "approval_needed",
      "title": "Approval needed",
      "value": true
    },
    {
      "name": "rule",
      "title": "Rule applied",
      "value": 1
    }
  ]
}

In a workflow this is a single step: n8n, Zapier, Make and Power Automate send the values and get the decision back (business logic in workflows). AI assistants such as Claude and ChatGPT use the same decision table as a tool over MCP (Excel MCP server).

The rules do not have to contain every number themselves: formulas can read a price list from your own system at each calculation (live data). Measured on our demo calculator, a calculation takes about 20 ms on the server. The Basic plan is free: one published service, 1,000 calls a month, workbooks up to 250 KB.

Decision table, DMN or a rules engine?

DMN (Decision Model and Notation) is a standard for describing decisions. Its decision tables have a defined format and a hit policy, which states what happens when several rules match: unique (only one rule may match), first (the first match wins) and collect (all matches are returned) are three of them. Dedicated tools model and run such tables.

A decision table in a spreadsheet is less formal. Its hit policy is whatever your formulas do: the XLOOKUP on the helper column above behaves like "first", and nothing stops two rows from overlapping. SpreadAPI adds the API and input validation. It has no version history with rollback, no approval workflow and no audit trail of who changed which rule.

If changes have to be approved, versioned and audited, use DMN tooling or a rules engine. If one specialist or one team can read and maintain the rules in a spreadsheet, publishing that spreadsheet is the shorter way. More on that boundary: a business rules engine, or simply a spreadsheet?

A good decision table: a checklist

Seven points to check before other systems depend on the table.

  • Every condition has its own column, with a clear name and unit.
  • Thresholds say which side they belong to: "from 2,500" includes 2,500.
  • Every possible case lands on a rule; a default row catches the rest.
  • Where rules overlap, the order is stated: the first match wins.
  • Outcomes are values a program can use: 0.15 and TRUE, not "about 15 %".
  • Every rule has a number, and the answer says which rule applied.
  • Known cases are written down and tested after every change.

Questions about decision tables

What is a decision table?

A decision table lists the conditions of a decision and the outcome for each combination of them. Each rule is one row or one column. It replaces nested if-then logic with a table that people can read, check for gaps and change.

What is the difference between a decision table and a decision tree?

Both describe the same logic. A decision tree asks one question after another and branches; it shows the order of the questions and becomes hard to survey as it grows. A decision table shows all rules side by side, so missing combinations and contradictions are easier to see. Every path of a tree from root to leaf is one rule of a table.

Can Excel be used for decision tables?

Yes. Write one rule per row and evaluate the table with lookup formulas: XLOOKUP or INDEX and MATCH for thresholds and grids, a helper column or FILTER for tables where the first matching rule wins. Excel does not check the table for gaps or overlaps; you do that with test cases.

How do I call a decision table from a workflow?

Publish the workbook with SpreadAPI and call it as one step: with the SpreadAPI node in n8n, the action in Zapier, the custom connector in Power Automate, or an HTTP step in Make and other tools. The step sends the condition values and gets the outcomes back as JSON.

Put your decision table behind an API.