PROCESIO
OverviewPlatform ActionsIntegrations & DemosCustom ActionsDeveloper’s Guide

Data Store

A table you define and fill inside a workspace — rows and columns you can read and write from a process, from a form, or through the API.

Behaviours in this guide were verified on 12 August 2026. The screenshots come from the automated test suite, which is why the example stores are named AUTO_DSACT_Store and AUTO_DOCS_Store.

What a Data Store is

A Data Store holds structured records — a customer list, a price table, a queue of things to process — that live in your workspace and persist between process runs.

You define the columns once, then add rows through the interface, a CSV import, a process, a form, or the API. Any process in the same workspace can read those rows, add to them, change them or remove them.

Data Store or Data Model?

They solve different problems. A Data Model describes the shape of data moving through a single process run — it holds no records of its own. A Data Store is where records actually live and stay. If you need the information to still be there tomorrow, or to be shared between runs, use a Data Store.

Creating a Data Store

Open Data Stores from the sidebar and choose Create. Give it a name, then define your columns.

The Data Stores list page, with a Create button in the top-right corner

The Data Store list. Each store shows its name and description; Create is top right.

Columns

SettingWhat it means
NameUp to 64 characters. This is what you will pick from every column list later, so make it readable — InvoiceNumber reads better than inv_no.
TypeOne of nine types — see Column types. Choose deliberately: changing a type later can affect rows that already exist.
Primary keyAt least one column must be a primary key. It identifies a row uniquely, so no two rows may share the same value. You can mark several columns as the key together (a composite key) when one column alone isn't unique.
RequiredA required column must have a value in every row. Adding a row without one is rejected.

Columns you get for free

Every Data Store automatically tracks four system columns. You cannot create, rename or write to them, but you can filter and sort by them:

ColumnHolds
CreatedOnWhen the row was first added
UpdatedOnWhen the row last changed
CreatedByIdWho added it
UpdatedByIdWho last changed it

Only those four exact names are reserved. A near-miss like created_on is accepted as an ordinary column, which is rarely what you want — it will sit alongside the real CreatedOn and confuse everyone later.

Editing the schema — saving a Data Store replaces its entire column definition. This is by design, and it is powerful enough to hurt: if you remove a column and save, that column's data is gone and cannot be recovered, and saving with every column removed empties the store the same way, without any extra confirmation. Export a CSV before making structural changes to a store that holds real data.

Column types

Nine types are available. There is no separate Date, Time, Decimal or File type — use DateTime or String instead.

TypeUse it forWorth knowing
StringNames, codes, notes, anything textualHolds up to 65,535 bytes. Accented and non-Latin characters count for more than one byte each.
IntegerCounts, quantities, whole numbers64-bit. A decimal is accepted and silently rounded — 2.5 becomes a whole number with no warning, and which way an exact half rounds differs between entry points. If the fraction matters, use Double; if it doesn't, round deliberately before writing.
DoublePrices, measurements, anything fractionalStandard 64-bit floating point.
FloatStored identically to Double. It does not save space or give you lower precision; prefer Double for clarity.
BooleanYes/no flagsWrite it as unquoted true / false — those two literals are accepted everywhere: adding rows, filtering, CSV. Numbers (1/0) and quoted text ("true") are each accepted in some places and rejected in others, so don't rely on them.
DateTimeDates and timestampsWrite as 2026-07-17T14:30:00. A date alone (2026-07-17) works too and means midnight. A trailing Z is rejected by the rows API.
GuidIdentifiers from other systemsValues are not validated. A malformed identifier is accepted and stored in a mangled form, so validate before writing.
JsonNested or variable structuresStored as JSON. Structure round-trips, but key order and whitespace are normalised, so don't compare the raw text.
ObjectSame as JsonBehaves identically to Json.

Date and time format — the accepted written form is YYYY-MM-DDTHH:mm:ss, for example 2026-07-17T14:30:00. Formats like 17/07/2026 14:30 or a Unix timestamp are rejected. Values are stored as written, without timezone conversion, so decide on one timezone convention for your workspace and stick to it.

Working with rows

Open a Data Store to see its rows. From here you can add records, filter, sort and page through the data, and — with the Read-Only switch flipped to Edit Mode — change it in place.

The Data Store viewer showing a grid of rows with typed column headers

The Data Store viewer. Each column header carries its type, the key column is marked, and required columns show an asterisk. Import and Export handle CSV; the Read-Only switch protects the data while you are just looking.

Filtering and sorting

Every column header has a small filter icon. Click it to open the column's menu: sort the grid A→Z or Z→A from the top, or build a filter below — pick an operator, give a value, and press Add. Each active filter appears as a chip above the grid; remove one chip, or Clear all, to widen the view again.

Which operators the menu offers depends on the column's type — text columns offer contains and starts with, number and date columns offer greater than, less than and between, and so on.

The column filter menu open on a DateTime column, with the operator list expanded

The filter menu on a DateTime column with its operator list open. Date columns take their value from a date picker plus a time picker — both matter, as explained below.

Text matching ignores case. Searching acme finds ACME and Acme. Sorting is case-insensitive too, so apple and Apple sort together rather than in separate blocks.

Filtering dates: pick a date and a time

On a date column, equals compares the exact instant — the date and the time together. If you pick only a date, the time part defaults to midnight (00:00), so the filter matches only rows stored at exactly midnight — against real data that is usually no rows at all, with no error to tell you why.

The filter menu on a DateTime column: an operator select plus separate Date and Time pickers

A date column's filter takes its value from two pickers — Date and Time. Leaving Time empty means 00:00, not "any time that day".

  • Looking for one exact record? Pick the date and the time with equals.
  • Looking for "everything on that day"? Use between — from the day at 00:00 to the day at 23:59 — or a pair of greater than / less than conditions.

A date filter that matches nothing — "Equals 17 July" with no time means "equals 17 July at 00:00:00 exactly". This is the single most common reason a date filter comes back empty while you can see matching rows on screen. Reach for between when you mean a whole day.

Paging

Rows come back a page at a time. Two behaviours are worth knowing if you page through data from a process or the API:

  • Asking for a page beyond the end returns the last page, not an empty one. Don't loop "until the page comes back empty" — it never will. Work out the number of pages from the total count instead.
  • A page size of 0 means every row, unpaged. Handy for small tables, risky for large ones.

Editing rows in the grid

The grid opens in Read-Only mode so nothing changes while you browse. Flip the switch to Edit Mode and the rows become editable in place: type into any cell, add new rows with Add Row, and remove a row with the bin icon at its end.

The Data Store grid in Edit Mode: cells rendered as inputs, an Add Row button, and commit/discard actions

Edit Mode. Cells become inputs, Add Row appends a record, and the bin icon on each row deletes it. Nothing is saved yet — the edits are held until you commit.

Your edits are collected, not saved one by one. The Commit Changes button counts them as you go. When you are done, press it and a Confirm changes dialog tells you how many rows will be written and reminds you the write cannot be undone. Yes, commit changes writes the whole batch together; Discard Changes throws it away and puts the grid back the way it was; Cancel returns to editing with the batch intact.

The Confirm changes dialog with Cancel, Discard Changes and Yes, commit changes buttons

The Confirm changes dialog guards every batch of edits: Yes, commit changes writes them, Discard Changes abandons them, Cancel keeps editing.

Uncommitted edits are lost on navigation — leaving the page, or switching back to Read-Only, without committing abandons the pending edits silently. If a row you "added" is missing later, this is almost always what happened: the commit step was skipped.

Two details to know: a new row must respect the schema — required columns need a value, and the primary key must be unique — and a cell edit that violates a rule is reported when you commit, not while you type. If the Edit Mode switch is greyed out, your account lacks the Data Store rows permission — see Who can see what.

Importing and exporting CSV

Both run as background jobs — you start them, and the file becomes available when the job finishes. Large files take a moment; nothing is lost if you navigate away.

Preparing a file to import

  • Comma-separated, UTF-8. A byte-order mark is tolerated.
  • The first row is a header of column names. They are matched by name, ignoring case and surrounding spaces, so column order doesn't matter.
  • Booleans as true / false in lower case.
  • Dates as 2026-07-17T14:30:00.

What happens to bad data

SituationResult
A header doesn't match any column, or a required column is missingThe whole import is rejected before anything is written.
Individual rows have bad valuesThe good rows are imported and the bad ones are collected into a failures file you can download, with the reason on each row.
A row's primary key already existsThe existing row is updated. Import is the supported way to bulk-update.

That last one differs from adding rows through a process or the API, where a duplicate key is an error rather than an update.

Exporting

Export produces the user columns only — the four system columns are not included. If you need CreatedOn in a spreadsheet, copy it into a column of your own.

Opening exports in a spreadsheet — cells beginning with =, +, - or @ are exported exactly as stored. Spreadsheet applications may treat them as formulas when the file is opened. If your data can contain values like that, open exports with an import step that treats every column as text.

Using a Data Store in a process

The Data Store action reads and writes rows as part of a process. Find it in the toolbox by searching for "Data Store" and drop it onto the canvas.

The Data Store action in the process designer toolbox

Search the toolbox for "Data Store" to find the action. If the toolbox shows shapes instead of actions, switch the toolbox source back to Actions — only that side has a search box.

Setting it up

  1. Click the action to open its panel.
  2. Pick the store under Select Data Store.
  3. Choose an Operation — Select, Insert, Update or Delete.
  4. Open Configure Operation to set up the details. The fields shown change with the operation you chose.

The Data Store action properties panel with a store selected and the Operation dropdown open

The action panel: pick the store, then the operation. Configure Operation opens the fields for the operation you chose.

Order matters — pick the store before the operation, and know these two quirks of the store list: a store created while the designer is open doesn't appear in the list until you reload the designer; and if you change the store on an action that was already configured, the previous store's columns stay on screen — re-pick the operation, Sort By and every Where column so the run doesn't fail on a column that no longer exists.

What each operation does

OperationYou configureYou get back
SelectPage Number, Page Item Count, Where, Sort By, DescResult Rows — the matching rows. Total Count — how many rows matched in total.
InsertSet Values — one entry per column you want to fillAffected Rows
UpdateSet Values (what to change) and Where (which rows)Affected Rows
DeleteWhere (which rows)Affected Rows

Update and Delete apply to every matching row — the Where filter is a filter, not a key lookup. An Update or Delete whose Where matches ten rows changes ten rows. Before a destructive run against real data, try the same Where on a Select first and check Total Count — it tells you exactly how many rows the write would touch.

Reading rows

Page Item Count defaults to 0, which returns every matching row. Set a number to page through instead. Total Count always reports how many rows matched your filter, not how many are on the current page — use it to work out how many pages there are. Like the viewer, a page number past the end returns the last page, never an empty one.

The Configure Operation panel for a Select with paging, Where, sorting and outputs

Configure Operation for a Select — paging, the Where builder, sorting, and the outputs.

Sort By lists your columns and the four system columns, so you can sort by CreatedOn to process the oldest records first. Tick Desc to reverse the order.

Writing rows

Set Values maps a column to the value you want written. Each entry can be a fixed value or a process variable, so an Insert can write data collected earlier in the run. Columns you leave out of the mapping are untouched by an Update.

The Set Values mapper shown for the Insert operation

Set Values for an Insert — one entry per column you want to fill.

A primary key column cannot be changed by an Update — put it in the Where filter to choose which row to change instead. Trying to set it produces a clear error telling you the same.

Using the results downstream

The outputs are ordinary process data. Bind Result Rows to a variable and a later action can loop over it, and Total Count can drive a Decisional — for example, branch when a lookup found nothing.

If an operation fails — a failing Data Store action stops the process unless you have connected its error path. Nothing is partially written: if an Insert is rejected, no rows are added at all.

Filtering with Where

The Where builder decides which rows a Select, Update or Delete applies to. Add a condition, pick the column, pick the operator, and give a value. Add more conditions and chain them with and / or. Picking the column also decides which operators the condition offers, so always pick the column first.

You can filter on the system columns too — CreatedOn after a given date is a common way to pick up recent records.

The Configure Operation panel with one Where condition configured on a number column

A condition on Qty — operator "greater than or equal", value 40. Once picked, the column is not redisplayed in the collapsed row (only the operator and value show) — click the left box when you need to check or change which column a condition points at. "Add condition" chains another; the badge between two conditions toggles and/or. Sort By, Desc and the outputs sit below.

Chaining conditions: and binds before or

With three or more conditions mixing and with or, the and pairs are evaluated first — exactly as the builder's own tooltip says: "AND has priority over OR in evaluating conditions." So

City = Cluj or Qty > 50 and Active is false

means City is Cluj, or (Qty over 50 and not active) — not "one of the first two, and also not active". If you mean the other grouping, put the or branch in its own group; a group is evaluated as one bracket.

Which operators you get

The list depends on the column's type:

Column typeOperators
StringEquals · Does not equal · Contains · Does not contain · Starts with · Ends with · Like · Not like · Belongs · Does not belong · Is empty · Is not empty
Integer, Double, Float, DateTimeEquals · Does not equal · Greater than · Less than · Greater than or equal · Less than or equal · Between · Is not between · Belongs · Does not belong · Is empty · Is not empty
BooleanIs true · Is false · Equals · Does not equal · Belongs · Does not belong · Is empty · Is not empty
GuidEquals · Does not equal · Belongs · Does not belong · Is empty · Is not empty
Json, ObjectEquals · Does not equal · Is empty · Is not empty

Operators that need a particular kind of value

  • Between and Is not between need two values — a lower and an upper bound. Both bounds are included in the match; leaving the second one out fails the run with a clear message.
  • Like and Not like match text with SQL wildcards: % stands for any run of characters, so %Pop% finds "Ana Pop" and "Ioana Popescu", and %Pop only values that end in "Pop". Without a wildcard, Like is an exact matchPop alone matches only the value "Pop". If you just want "text appears anywhere", Contains is the simpler operator.
  • Belongs and Does not belong match against a list. Provide it as a JSON array, for example ["Draft","Pending"]. A plain comma-separated list is not accepted.
  • Is empty matches rows where the column has no value at all. A column holding an empty piece of text is not "empty" in this sense.
  • On a date column, Equals compares date and time — the same rule as the viewer's filter menu; see Filtering dates. Use Between for a whole day.

Filtering a yes/no column — use Is true and Is false. On current builds, Equals true in a process Where returns the rows where the value is false — and reports no error. This matters most on Update and Delete, where it would change the wrong records. (Filtering a yes/no column in the viewer or in a form is not affected.)

Changing a condition's column — to point an existing condition at a different column, delete the condition and add a fresh one. Re-picking a column on a condition that already has one adds a second operand instead of replacing the first, and the condition stops meaning what it reads as.

Using a Data Store in a form

A form can read from and write to a Data Store through an event. The usual pattern is to show stored records in a table when the form opens; the same mechanism can add, update and delete records from the form's fields.

Showing records in a table

  1. Add a table element to the form.
  2. On the form, add an event with the trigger Form load and the action Run Data Store operation.
  3. Choose the store and the operation Read.
  4. Map the returned rows to the table's value.

A Read loads page 1 with 1,000 records per page by default. For a bigger store, raise Records per page, or better, add row filters so the form only loads what it shows.

One row showing instead of all of them? The table's row template must be a dynamic row. A static row renders exactly once no matter how many records come back — this is the single most common cause of "only the first record appears".

A public PROCESIO form displaying Data Store records in a table

A public form whose Form load event reads a Data Store into a table. The pager reports the number of records the read returned.

Filtering what the form reads

A Read event doesn't have to load the whole store. Open the event's Map data and filter rows panel — the row carries a Filters configured / not configured badge — and use Row filters at the bottom: + Add condition adds one condition (column, operator, value), + Add group adds a bracketed sub-group, and the badge between conditions toggles and/or.

The form builder's Map data side panel with the Row filters section

The Row filters editor inside the event's mapping panel. It is the same condition builder the process designer uses, so the operators per column type match the Where tables above.

The same and-before-or rule applies — silently. Row filters is a flat list with no brackets drawn, but mixed conditions still evaluate and pairs first. City = Cluj or Qty > 50 and Active is false loads the Cluj rows or the (over-50 and inactive) rows — fewer than a left-to-right reading suggests, with no warning. When mixing and with or, put each or branch in its own group so the grouping is explicit.

List operators in form Row filters — avoid Belongs / Does not belong in this panel for now: a list authored here reaches the form wrapped the wrong way, so belongs shows no rows and does not belong shows everything. Use several equals conditions joined with or instead — same result, reliable.

Writing from a form

The same event action offers Add, Update and Delete, typically on a button click, with the form's fields mapped to columns. All four operations work end to end: Add writes a new row (the primary key must be unique, exactly as everywhere else), Update changes the rows its filter matches, and Delete removes them.

Public forms

A form shared publicly reads and writes its Data Store without the visitor signing in. That access is granted workspace-wide, so the safe way to think about it is: any store in the same workspace as a public form may be reachable from the outside. Keep public forms — and the stores they use — in a workspace of their own, holding only data you are comfortable exposing, and never a store with personal or internal records.

Who can see what

Access to Data Stores is governed by workspace permissions, and viewing is separate from editing: an account can be allowed to see stores and rows yet not change them — for such an account the viewer's Edit Mode switch is disabled. A member with no Data Store permission at all cannot list stores, view their structure, or read their rows — those requests are refused rather than returning an empty result.

Empty store list? If the store list is empty where you expect stores — in a form's event configuration, for instance — it usually means the account lacks Data Store permission rather than that no stores exist. Ask a workspace administrator to check your access.

Troubleshooting

My filter returns nothing, but I can see matching rows

Check the operator and the value shape. On a date column, equals with only a date means midnight exactly — use between for a whole day. With Like, no wildcard means an exact match — add % (%Pop%), or use Contains. With Belongs, the value must be a JSON array such as ["A","B"]. And on a yes/no column in a process Where, use Is true / Is false rather than Equals.

My edits in the grid disappeared

Edits made in Edit Mode are held until you press Commit changes and confirm with Yes, commit changes. Navigating away — or answering the dialog with Discard Changes — abandons the whole batch.

My process fails saying a column doesn't exist

The action is pointing at a different store than the column belongs to — usually after changing Select Data Store on an action that was already configured. Re-open Configure Operation and re-pick Sort By and each Where column.

The process saved fine but does nothing when it runs

A Where condition whose operator was never chosen is saved without complaint. Open the condition and confirm an operator is selected.

My form's table only shows one record

The table's row template is static. Change it to a dynamic row — see Using a Data Store in a form.

My form filter on a list of values shows nothing (or everything)

That's the Belongs / Does not belong limitation in form Row filters — replace the list with several equals conditions joined by or. See Filtering what the form reads.

My CSV import didn't add anything

If a header doesn't match a column name, or a required column is absent, the entire import is rejected before any row is written. Compare your header row against the column names, then download the failures file for per-row reasons.

Dates are being rejected

Use 2026-07-17T14:30:00. A trailing Z, or a format like 17/07/2026, is not accepted when writing rows.

On this page