Item List Prefill
Allows an Item List node to present a pre-populated table of candidate items to the user before they start adding iterations. Each candidate row can be selected as the starting point for a new iteration — the candidate's data is written to the carousel's #Start node, where iteration questions can reference it as default values. Once a candidate has been used to fill an iteration it is flagged as Used, allowing the UI to hide or de-emphasise already-consumed rows.
The most common data source is the OCR Extraction Logic Item, which reads uploaded receipt images and produces a JSON array of line items that is stored in the conversation value bag under the key Items. That array is then forwarded to the Item List node's PrefillData input by referencing [ocr.Items].
How it works end-to-end
OCR Logic Item
└─ extracts Items from uploaded receipts
└─ stored in value bag as JSON array
Item List node (ListFromCarousel type)
└─ PrefillData = "[ocr.Items]" ← resolved at runtime to the OCR array
└─ PrefillTableDefinition ← describes which columns to display
GET /carousels/{id}/iterations
└─ ?prefillCandidateId={hash} ← pick a candidate row
SectionPlayer writes candidate fields to #Start node
└─ Start.Description, Start.TotalPrice, …
Iteration questions use Start.* as default values
POST /carousels/{id}/iterations/{n}
└─ ?prefillCandidateId={hash} ← must be repeated on save
Candidate is marked as Used in the carousel iteration value bag
└─ PrefillTable.DataRows[n].Used = true (next GetPrefillTable call)
Node input configuration
The Item List node receives its prefill configuration via ListFromCarouselNodeInput.
| Field | Type | Description |
|---|---|---|
PrefillData | string (JSON array) | Serialized array of objects. Each object is one prefill candidate. Typically populated from the OCR value bag with [ocr.Items]. |
PrefillTableDefinition | string (JSON array of ColumnDefinition) | Defines which columns appear in the prefill table and which property of each candidate maps to each column. |
ShowTotalSum | string ("true" / "false") | Whether to show a sum row at the bottom of the prefill table. |
TotalSumLabel | string | Label displayed next to the total sum. |
TotalSumUnits | string | Units displayed next to the total sum (e.g. "EUR"). |
Column definition (ColumnDefinition)
Each entry in PrefillTableDefinition controls one column of the prefill table.
| Field | Type | Description |
|---|---|---|
Title | string | Column header text (can be a message key resolved from conversation history). |
Value | string | Dot-notation path into the candidate object, e.g. "Description" or "tax.rate". |
IsSummable | bool | Whether values in this column contribute to the total sum row. |
MessageType | string? | Visual type hint for the cell (defaults to InputText). |
Example PrefillTableDefinition
[
{ "title": "Description", "value": "Description", "isSummable": false },
{ "title": "Amount", "value": "TotalPrice", "isSummable": true },
{ "title": "Tax rate", "value": "tax.rate", "isSummable": false }
]
The prefill table (PrefillItemListTable)
When the Item List node is initialised and EnableItemListPreliminaryItems is enabled, ItemListLogicItem exposes a PrefillTable property on the API response.
| Field | Type | Description |
|---|---|---|
CarouselNodeId | string | ID of the carousel node this table belongs to. |
Columns | Dictionary<int, ItemListColumn> | Column definitions keyed by column index. |
DataRows | PrefillItemListRow[] | One row per candidate item. |
ShowTotalSum | bool | Mirrors the node input setting. |
TotalSumLabel | string | Mirrors the node input setting. |
TotalSumUnits | string | Mirrors the node input setting. |
Prefill candidate row (PrefillItemListRow)
| Field | Type | Description |
|---|---|---|
PrefillCandidateId | string | Stable content-derived identifier for this candidate (8-char Base64url-encoded MD5 of the sorted item JSON). Survives re-processing — two rows with identical content always produce the same ID. Pass this value as prefillCandidateId on both API calls. |
Used | bool | true if an active (non-stale) iteration has already been filled from this candidate. Use this to hide or grey-out already-consumed rows in the UI. |
Cells | Dictionary<int, string> | Column index → display value for this row. |
Content hash identity
PrefillCandidateId is computed as:
- Sort the candidate object's properties alphabetically (recursively for nested objects).
- Serialize to compact JSON.
- Compute MD5 of the UTF-8 bytes.
- Encode as Base64url, take the first 8 characters.
This means the ID is position-independent — reprocessing the OCR or reordering the source array does not change the ID for an unchanged item. Two different items with identical content hash to the same ID; in practice this is safe for any realistic prefill dataset.
API usage
1. Fetch the prefill table
The PrefillTable is returned as part of the standard Item List node payload whenever the node is fetched. No additional call is needed.
GET /api/v1/conversations/{conversationId}/sections/{sectionId}
Response includes the prefillTable property on the Item List visual node when candidates are available.
2. Create a prefilled iteration
Pass the chosen PrefillCandidateId as a query parameter when requesting a new iteration.
GET /api/v1/conversations/{conversationId}/carousels/{carouselNodeId}/iterations
?prefillCandidateId={hash}
Effect:
The backend resolves the candidate item whose PrefillCandidateId matches the hash, flattens all its properties using dot-notation (e.g. tax.rate → key "tax.rate"), and writes each key-value pair into the #Start node's value bag for the new iteration.
Iteration questions can then reference those values using the Start. prefix, for example:
| Reference in question | Resolves to |
|---|---|
Start.Description | "Wireless Mouse" |
Start.TotalPrice | "29.90" |
Start.tax.rate | "21" |
This pre-populates the question's default value, which the user can accept or override.
3. Save the iteration with the same candidate ID
When saving a completed iteration, pass the same prefillCandidateId query parameter.
POST /api/v1/conversations/{conversationId}/carousels/{carouselNodeId}/iterations/{iterationId}
?prefillCandidateId={hash}
Body: { "visualNodeResults": [ … ] }
Effect:
On successful save the candidate ID is stored in the iteration's value bag under __itemListPrefillCandidateId. The next call to GetPrefillTable will compare that stored hash against every candidate and set Used = true on any matching row.
Important: if
prefillCandidateIdis omitted on the save call the candidate will not be marked as used, even if it was supplied on the get call.
Used flag and UI behaviour
The Used flag on each PrefillItemListRow reflects whether an active (non-stale) iteration was created from that candidate.
- If an iteration is later edited and re-saved without a candidate ID, the stored hash remains and the candidate stays marked as used.
- If an iteration is deleted (marked stale), its stored hash is excluded from the used-set calculation and the candidate becomes available again.
- The flag is read-only — it is recomputed fresh on every
GetPrefillTablecall from the current carousel state.
Recommended UI behaviour:
Used | Suggested presentation |
|---|---|
false | Show row normally; allow selection |
true | Grey out / hide row; optionally show a "already added" indicator |
Full example — OCR receipt to prefilled iteration
Setup
Conversation properties on the OCR node:
ocr-Items = ""
ocr-MerchantName = ""
ocr-TransactionDate = ""
Item List node input:
{
"prefillData": "[ocr.Items]",
"prefillTableDefinition": "[{\"title\":\"Description\",\"value\":\"Description\",\"isSummable\":false},{\"title\":\"Amount\",\"value\":\"TotalPrice\",\"isSummable\":true}]",
"showTotalSum": "true",
"totalSumLabel": "Total",
"totalSumUnits": "EUR",
"carouselNodeName": "vWGkRI"
}
Runtime flow
1. OCR runs and stores in value bag:
Items = "[{\"Description\":\"Wireless Mouse\",\"TotalPrice\":\"29.90\",\"MerchantName\":\"Demo Store\",\"TransactionDate\":\"2026-05-07\"}]"
2. Item List node is fetched — PrefillTable in response:
{
"prefillTable": {
"carouselNodeId": "vWGkRI",
"columns": {
"0": { "key": 0, "title": "Description", "isSummable": false },
"1": { "key": 1, "title": "Amount", "isSummable": true }
},
"dataRows": [
{
"prefillCandidateId": "Or8sHQ2k",
"used": false,
"cells": { "0": "Wireless Mouse", "1": "29.90" }
}
],
"showTotalSum": true,
"totalSumLabel": "Total",
"totalSumUnits": "EUR"
}
}
3. User picks the row — UI calls:
GET /api/v1/conversations/conv1/carousels/vWGkRI/iterations
?prefillCandidateId=Or8sHQ2k
The #Start node for the new iteration is populated:
Start.Description = "Wireless Mouse"
Start.TotalPrice = "29.90"
Start.MerchantName = "Demo Store"
Start.TransactionDate = "2026-05-07"
Iteration questions referencing Start.Description show "Wireless Mouse" as the default value.
4. User confirms — UI calls:
POST /api/v1/conversations/conv1/carousels/vWGkRI/iterations/1
?prefillCandidateId=Or8sHQ2k
{ "visualNodeResults": [ … ] }
5. Next GetPrefillTable call returns:
{
"prefillCandidateId": "Or8sHQ2k",
"used": true,
"cells": { "0": "Wireless Mouse", "1": "29.90" }
}
The UI hides or disables the row.
Feature flag
The prefill feature is gated by ICoreFeatureToggleSettings.EnableItemListPreliminaryItems.
When the flag is false, PrefillTable is always null and prefill candidate IDs passed to the API are ignored.