Get Data
What does it do?
Reads a single document from the customer-scoped data store by collection + key. If the row is missing or already expired, the result is null. The loaded value is written to bot state under this node's name.
A missing record is not a failure — the node still routes to on_complete with null in state. To branch on existence, follow get with a matchExpression (see Examples). on_failure only fires on configuration errors (e.g. invalid params), not on "not found".
For querying many rows at once, use list instead.
1. Syntax
<node_name>:
type: func
func_type: dataStorage
func_id: get
params:
collection: "<collection_name>"
key: "<record_key>"
on_complete: <next_node>
required params
typetype of the nodefunc_typehere it will bedataStoragefunc_idwhat function are we calling (get)params.collectioncollection name (non-empty, max 100 chars)params.keyrecord key within the collection (non-empty, max 200 chars)on_completenext node after the read finishes
optional params
on_failurefallback nodedepartmentassigns the chat to a departmentagentassigns the chat to a specific agent (email address or CRM ID as defined in the Texter agents manager)
2. Output
The loaded item (or null) is stored at %state:node.<node_name>%. When a record exists:
{
"_id": "...",
"collection": "sla_chats",
"key": "972501234567",
"data": { "timestamp": 1715520000000, "chatId": "..." },
"tags": ["urgent"],
"expiresAt": 1715606400000,
"createdAt": 1715520000000,
"updatedAt": 1715520000000
}
Common access paths:
%state:node.<name>.data%— the stored payload object%state:node.<name>.data.<field>%— a specific field inside the payload%state:node.<name>%— the whole DTO (ornullif not found / expired)
3. Examples
Load a record by stable key, then branch
load_sla_record:
type: func
func_type: dataStorage
func_id: get
params:
collection: "sla_chats"
key: "%chat:channelInfo.id%"
on_complete: check_if_timer_started
Use injected key from another node
fetch_prefs:
type: func
func_type: dataStorage
func_id: get
params:
collection: "user_prefs"
key: "%state:node.ask_account_id.text%"
on_complete: apply_theme
Branch on whether a record exists (common pattern)
Because get routes to on_complete even when the item is null, the standard pattern is to follow it with a matchExpression that checks existence, then act on .data only in the "found" branch.
smart_resolved:
type: func
func_type: dataStorage
func_id: get
params:
collection: "ChatsLastAgent"
key: "%chat:_id|toString%"
on_complete: check_if_item_exists
on_failure: noop_back_to_agent
check_if_item_exists:
type: func
func_type: system
func_id: matchExpression
params:
expression: "exists(item)"
item: "%state:node.smart_resolved%"
on_complete: handoff_to_last_agent
on_failure: noop_back_to_agent
handoff_to_last_agent:
type: func
func_type: system
func_id: noop
agent: "%state:node.smart_resolved.data.latestAgentUid%"
on_complete: handoff
noop_back_to_agent:
type: func
func_type: system
func_id: noop
on_complete: back_to_agent
Here on_failure on the get node only catches setup-level errors; the "record not found" case is handled by the matchExpression right after.
Expired rows are treated the same as missing — %state:node.<name>% is null. Always follow get with an existence check (matchExpression with exists(...)) before reading .data.