The Data Table node is how a flow works with the rows of a data table while a conversation is running. It sits in the Flow Designer palette under Service Request, and one node performs one operation: read matching rows, add a row, update matching rows, update or insert, or delete matching rows. To read a value and then write one back, use two nodes.
This article covers configuring the node, what it writes into your variable and which output port the flow leaves through. What a data table is for, where its edges are and the limits that apply everywhere are on Data Tables. Creating a table and defining its columns is covered in Creating a Data Table.
The five operations
Operation is the first choice on the node, and it decides which other sections appear. All five option names stay in English in every interface language.
| Operation | What it does | Conditions | Values | The variable receives |
|---|---|---|---|---|
| Get | Reads matching rows | Optional | Not used | A JSON array of the matching rows, or a plain number with Return count only on |
| Insert | Adds one new row | Not used | Values to Insert, at least one, pre-filled | A JSON array holding the one row that was inserted |
| Update | Updates every matching row | At least one | Values to Update, at least one, never pre-filled | The number of rows updated |
| Upsert | Updates matching rows, or inserts one when nothing matches | At least one | Values to Upsert, at least one, pre-filled | The number of rows written, whether they were updated or inserted |
| Delete | Deletes every matching row | At least one | Not used | The number of rows deleted |
Output ports
The node has three output ports: found, notFound and failed.
| Port | The flow leaves through it when |
|---|---|
| found | Get: at least one row matched, or, with Return count only on, the count is more than zero. Insert: the row was inserted, which is every successful Insert. Update: at least one row was updated. Upsert: rows matched and were updated. Delete: at least one row was deleted. |
| notFound | Nothing matched the conditions. Nothing was changed, and the counting operations write zero. On an Upsert this is the insert branch: nothing matched, so one row was inserted and the variable holds 1. |
| failed | The operation could not be carried out. See When the node fails below. |
Zero matching rows is not a failure. An empty result takes notFound, and there is no separate empty port. A Get with Return count only on behaves the same way: a count of zero takes notFound.
When you drop the node on the canvas, only found is linked. If the operation ends on a port you have not linked, the flow continues through found instead, so link notFound and failed wherever the difference matters.
Conditions
Conditions select the rows an operation works on. Get, Update, Upsert and Delete all use them; Insert does not, because it matches nothing.

1. Matching rule: How several conditions combine. All conditions (AND) is the default; Any condition (OR) matches a row that satisfies at least one. Not shown for Insert.
2. Column: A column of the selected table, followed by the three system columns Id, DateCreated and DateModified, which can be used in a condition as well. Each entry shows the column type beside the name.
3. Operator: What is offered depends on the column's type. See the table below.
4. Value: A static value or a variable. Is empty and Is not empty take no value, and the field is replaced by a note saying so.
Operators by column type
| Column type | Operators |
|---|---|
| Text | Equals, Not equals, Starts with, Starts with (case sensitive), Is empty, Is not empty |
| Number | Equals, Not equals, Less than, Less than or equal, Greater than, Greater than or equal, Is empty, Is not empty |
| DateTime | Equals, Not equals, Before, On or before, After, On or after, Is empty, Is not empty |
| Boolean | Equals, Not equals, Is empty, Is not empty |
| Long Text | None |
Id (system column) |
Equals, Not equals |
DateCreated, DateModified (system columns) |
The DateTime operators above |
A Long Text column cannot be used in a condition at all. If a flow needs to filter on a piece of text, keep that column as Text.
Conditions and sorting are fastest on columns marked with Fast filter when the table was defined. On a table with tens of thousands of rows, filtering or sorting on an unindexed column is noticeably slower.
Get: reading rows

1. Data Table: The table this node works on.
2. Operation: Get.
3. Matching rule and 4. Conditions: Optional here. With no conditions, the operation reads the table from the start, up to Return Limit.
5. Columns to Return: Which columns are written into the variable. Leave it empty to return every column, including Id, DateCreated and DateModified.
6. Sort by: A column and a direction, Ascending or Descending, applied before the limit, so the rows you get back are the top results in the order you chose.
7. Return Limit: How many rows at most are returned. Default: 25 (range 1 to 500).
8. Return count only: Off by default. The node's only toggle, and offered for Get alone.
9. Response Content: The variable the result is written to.
With the toggle off, the variable holds a JSON array of the matching rows, and an empty array when nothing matched. With it on, the variable holds a plain number, and Sort by, Columns to Return and Return Limit disappear from the panel because none of them applies to a count.

Turn Return count only on when the flow only needs to know whether something exists or how many there are, for example how many open tickets a customer has. It avoids carrying rows the flow never reads.
Insert: adding a row

1. Data Table and 2. Operation: Insert. There is no conditions section and no matching rule.
3. Values to Insert: Mandatory, and pre-filled with every column you defined, so configuring it is mostly a matter of deleting the rows you do not want to set. A column left empty is saved as null.
4. Response Content: Receives a JSON array holding the one row that was inserted, which is how the flow gets the new row's Id.
A successful Insert always leaves through found.
Update: changing matching rows

1. Data Table and 2. Operation: Update.
3. Matching rule and 4. Conditions: At least one condition is required. Every row that matches is updated, not just the first.
5. Values to Update: At least one column and value. Unlike Insert and Upsert, this list starts empty. Columns you do not add keep the value they already have.
6. Response Content: Receives the number of rows updated.
If nothing matches, the flow leaves through notFound with zero in the variable. That is not a failure, so a flow that must react to it needs the notFound port linked.
Upsert: update, or insert when nothing matches
Upsert is configured like Update, and behaves like Update whenever its conditions match. When they do not, it inserts a single row from the same values.

1. Data Table and 2. Operation: Upsert.
3. Matching rule and 4. Conditions: At least one condition is required, and these select the rows to update.
5. Values to Upsert: At least one value, and pre-filled with every column, as on Insert.
6. Response Content: Receives the number of rows written, on either branch.
The port tells you which branch ran: found means rows matched and were updated, notFound means nothing matched and one row was inserted. If both outcomes should continue down the same path, link both ports to it.
⚠️ Warning: The insert branch writes only the columns present in Values to Upsert. Any column you removed from the pre-filled list is saved as null on that new row, even though the same removal leaves an existing row untouched on the update branch. Keep the columns that identify the record in the list, including the ones your conditions match on.
Delete: removing matching rows

1. Data Table and 2. Operation: Delete.
3. Matching rule and 4. Conditions: At least one condition is required. There is no values section.
5. Response Content: Receives the number of rows deleted, and zero on notFound.
⚠️ Warning: Every row matching the conditions is deleted permanently, and there is no undo and no recycle bin anywhere in the product. A broad condition combined with Any condition (OR) can empty most of a table in one call. Test the same conditions with a Get first, and read the count before you switch the node to Delete.
When the node fails
On failure the node empties the response variable (an empty list, or zero for the counting operations) and routes the flow out of the failed port. The reason is recorded only in the platform's server logs, and the node exposes no per-conversation detail you can open to see why an operation failed, so link failed to something that reacts rather than letting it fall through.
What takes the failed port:
- A database error or a timeout.
- A value that does not fit its column's type.
- The table's row limit or a storage limit being reached, on an Insert or on the insert branch of an Upsert.
- A required condition missing at runtime, for example a condition whose only value came from a variable that was empty.
- A Get result larger than 5 MB.
Values are never type-checked while you design the flow, because they may hold variables. A value that cannot be stored in its column therefore appears as a failed at runtime and not as a warning in the designer.
What the designer checks before you publish
An incompletely configured node carries a warning badge, and publishing the project reports it by node and flow name. The rule per operation:
| Operation | Conditions | Values |
|---|---|---|
| Get | Optional | Not applicable |
| Insert | Not applicable | At least one required |
| Update | At least one required | At least one required |
| Upsert | At least one required | At least one required |
| Delete | At least one required | None accepted |
A condition also has to name a column that still exists in the table, use an operator its column type allows, and carry a value unless the operator is Is empty or Is not empty. A value mapping cannot name a system column, and cannot name the same column twice.
Limits
| Item | Value |
|---|---|
| Return Limit (Get) | Default 25, range 1 to 500 |
| Result written to the variable (Get) | Up to 5 MB, larger takes the failed port |
| Rows added by one Insert, or by the insert branch of an Upsert | 1 |
| Worst-case duration of one operation | About 15 seconds, after which the node takes the failed port |
The table's own limits, on rows, storage and value length, apply to what a flow writes exactly as they apply to the admin screens. They are listed on Data Tables.
