Elizon Docs
Data Tables

Remembering where a run stopped

A one-row Data Table is the place for a "last processed" time, a cursor or an "already sent today" flag.

Some workflows need one small fact from the last run: when did I last look?, where did I stop?, did I already send today's reminder? A Variable node cannot hold it, because a variable lives for one run only. A Data Table can.

There is no separate key-value store for this on purpose. A Data Table already has a project scope, permissions, Package support and a Studio page where a person can see the value and fix it. A state table is just a table with one row.

The table

Create a table called Workflow state with two columns:

ColumnTypeWhat it holds
KeyStringA name you choose, for example contract-monitor
ValueStringThe remembered text: a timestamp, a cursor, a flag

One table can serve every workflow in the project, one row per Key.

Reading the value

A Row · Get row(s) node with one condition, Key equals your key. If the table has no such row yet, the node returns no rows, which is the right answer for a first run: treat it as "never ran".

node data — Row · Get the remembered value
{
  "resource": "row",
  "operation": "get",
  "tableId": "5c1d7b30-2e4f-4a58-8f0b-3a9e6d12c777",
  "tableName": "Workflow state",
  "conditions": [
    {
      "id": "cond-1",
      "column": "Key",
      "operator": "eq",
      "value": "contract-monitor",
      "valueMode": "fixed"
    }
  ],
  "conditionMatch": "all",
  "returnAll": true
}

Writing it back

A Row · Upsert row node at the end of the workflow, with the same condition as its key. Run it last so that a run that fails halfway does not move the marker past work it never did.

node data — Row · Remember where this run stopped
{
  "resource": "row",
  "operation": "upsert",
  "tableId": "5c1d7b30-2e4f-4a58-8f0b-3a9e6d12c777",
  "tableName": "Workflow state",
  "conditions": [
    {
      "id": "cond-1",
      "column": "Key",
      "operator": "eq",
      "value": "contract-monitor",
      "valueMode": "fixed"
    }
  ],
  "conditionMatch": "all",
  "mapping": "manual",
  "mappingRows": [
    { "id": "map-1", "column": "Key", "value": "contract-monitor", "valueMode": "fixed" },
    {
      "id": "map-2",
      "column": "Value",
      "value": "{{ upstream.now-1.output.text }}",
      "valueMode": "expression"
    }
  ]
}

The first run inserts the row and every later run updates it, so there is never a second row for the same key.

Three common uses

  • Last processed time. Store a timestamp, then filter the next run's source on "newer than the stored value".
  • A pagination cursor. Store the cursor an API returned, and send it back on the next run.
  • Already sent today. Store the date you last sent. A first node reads it, and an If node skips the send when it equals today's date.

What this does not do

Two runs of the same workflow that overlap can both read the old value. If that matters, do not let the runs overlap, or key the state by something each run owns. Polling triggers (Email, Knowledge Base, apps) keep their own cursor for you and do not need this.