Querying Fs Data Capture Reference
SkillCloud & infraGuides your agent to build and deploy Salesforce Field Service offline mobile forms as Data Capture Flows.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Querying Fs Data Capture Reference skill
About this capability
Build, edit, and deploy Salesforce Data Capture Flows (processType DataCaptureFlow), Field Service mobile / offline forms. Use when authoring flow-meta.xml with runtime_service_fieldservice:dc* components, Repeater loops (.AllItems), master-detail child record persistence, visual polish (gradient b
What this skill tells your AI
The instructions your AI receives, as published by forcedotcom/sf-skills in skills/field-service-data-capture-reference-configure/SKILL.md and read by ahel’s review.
When to Use This Skill
Build, edit, and deploy Salesforce Data Capture Flows (processType DataCaptureFlow) — Field Service mobile / offline forms. Use when authoring flow-meta.xml with runtime_service_fieldservice:dc* components, Repeater loops (.AllItems), master-detail child record persistence, visual polish (gradient banners, progress bars, callouts), supporting objects with FLS/permsets, debugging DataCaptureFlow deploy errors, or troubleshooting why a deployed form doesn't appear on the FSL Mobile Forms tab (DDC/WorkPlan OWD + AssignedResource sharing prerequisites).
Workflow
Salesforce Data Capture Flow Skill
Build, edit, and deploy Salesforce Flows with processType: DataCaptureFlow (Field Service mobile / offline forms).
Required metadata (every flow)
<processType>DataCaptureFlow</processType>
<areMetricsLoggedToDataCloud>false</areMetricsLoggedToDataCloud>
<environments>Offline</environments>
<!-- NO <apiVersion> tag -->
Optional IsLlmTargetable custom property — if you include it, it must be a JSON string, not a boolean:
<customProperties>
<name>IsLlmTargetable</name>
<value><stringValue>{"value":"false"}</stringValue></value>
</customProperties>
The <booleanValue>false</booleanValue> form deploys but blocks activation — error: The value of the IsLlmTargetable custom property's value field must be a string in JSON format. Omitting the property entirely is also fine.
XML structure rules
Salesforce's Flow schema enforces grouping — all elements of the same type must appear in a single contiguous block. Deploy fails with Element X is duplicated at this location when violated.
Group order doesn't matter, but within each group elements must be adjacent:
- all
<choices>together - all
<dynamicChoiceSets>together - all
<screens>together - all
<decisions>together - all
<recordLookups>together - all
<recordCreates>together - all
<recordUpdates>together - all
<loops>together - all
<assignments>together - all
<variables>together
Connector references determine execution order, not XML order.
Component reference
All extensions: prefix runtime_service_fieldservice:
| Component | Extension | fieldType |
|---|---|---|
| Short Text | dcTextInput | ComponentInstance |
| Long Text | dcLongText | ComponentInstance |
dcEmail | ComponentInstance | |
| Phone | dcPhone | ComponentInstance |
| Name | dcName | ComponentInstance |
| Numeric | dcNumeric | ComponentInstance |
| Counter | dcCounter | ComponentInstance |
| Date | dcDate | ComponentInstance |
| Date & Time | dcDateTime | ComponentInstance |
| Checkbox | dcCheckbox | ComponentInstance |
| Toggle | dcToggle | ComponentInstance |
| Address / GPS | dcAddress | ComponentInstance |
| Lookup | dcLookup | ComponentInstance |
| Static image | dcFileView | ComponentInstance |
| Upload image (mobile) | dcUpImage | ComponentInstance |
| Upload file (mobile) | dcUpFile | ComponentInstance |
| Signature (mobile) | dcSignature | ComponentInstance |
| Picklist single | dcPicklist | ComponentChoice |
| Picklist multi | dcPicklist | ComponentMultiChoice |
| Radio buttons | dcRbGroup | ComponentChoice |
| Checkbox group | dcCbGroup | ComponentMultiChoice |
| Matrix | dcMatrix | ComponentMultiChoice |
| Display text | (none) | DisplayText |
| Section | (none) | RegionContainer + Region |
| Repeater | (none) | Repeater |
Note: <fieldType>Range</fieldType> is NOT a valid slider fieldType in DataCaptureFlow (despite "Range/Slider" appearing in Builder UI lists). Sliders aren't available as pure metadata in this process type — use dcNumeric or dcCounter. forceContent:repeater (Lightning generic) ≠ <fieldType>Repeater</fieldType> (FSL offline). They share a concept, not XML.
Setting the label
| fieldType | How |
|---|---|
ComponentInstance | <inputParameters><name>label</name><value><stringValue>…</stringValue></value></inputParameters> |
ComponentChoice / ComponentMultiChoice | <fieldText>Label</fieldText> |
DisplayText | <fieldText>HTML</fieldText> |
All ComponentInstance extensions (including dcAddress and dcToggle) accept the label inputParameter — no wrapping DisplayText needed.
Required flag
Every input field needs <isRequired>true/false</isRequired> at field level. That single flag is sufficient for every dc* component — no extra required / isRequired inputParameter is needed.
dcLookup additionally accepts an isRequired inputParameter (boolean), but the field-level <isRequired> drives enforcement.
dcCheckbox and dcToggle accept <isRequired>true</isRequired> syntactically but don't enforce it at runtime.
Every input field must also have
<inputsOnNextNavToAssocScrn>UseStoredValues</inputsOnNextNavToAssocScrn>
<storeOutputAutomatically>true</storeOutputAutomatically>
<styleProperties>
<verticalAlignment><stringValue>top</stringValue></verticalAlignment>
<width><stringValue>12</stringValue></width>
</styleProperties>
Exception: <fieldType>Repeater</fieldType> explicitly rejects storeOutputAutomatically ("the storeOutputAutomatically field isn't supported"). Repeater output is always available as .AllItems — no opt-in needed.
Screen rules
Every screen needs ALL THREE of these or the Next/Finish button won't render:
<allowFinish>true</allowFinish>(even on non-final screens)<showFooter>true</showFooter><nextOrFinishButtonLabel>Next</nextOrFinishButtonLabel>
Plus:
- First element after
<start>must always be a<screens>element - All
<screens>elements must be grouped together in the XML
Repeater — iterating rows and creating child records
The Repeater's output collection is exposed as .AllItems. Confirmed working on API v66.
<!-- On the screen: -->
<fields>
<name>MyRepeater</name>
<fieldType>Repeater</fieldType>
<!-- NO storeOutputAutomatically on the Repeater itself -->
<fields>
<name>Row_PartName</name>
<extensionName>runtime_service_fieldservice:dcTextInput</extensionName>
<fieldType>ComponentInstance</fieldType>
<storeOutputAutomatically>true</storeOutputAutomatically>
<!-- … -->
</fields>
<fields>
<name>Row_PartQty</name>
<extensionName>runtime_service_fieldservice:dcCounter</extensionName>
<fieldType>ComponentInstance</fieldType>
<storeOutputAutomatically>true</storeOutputAutomatically>
<!-- … -->
</fields>
<isRequired>false</isRequired>
</fields>
<!-- At end of flow: -->
<loops>
<name>Loop_Parts</name>
<collectionReference>MyRepeater.AllItems</collectionReference> <!-- ← THE KEY -->
<iterationOrder>Asc</iterationOrder>
<nextValueConnector>
<targetReference>Create_Part</targetReference>
</nextValueConnector>
</loops>
<recordCreates>
<name>Create_Part</name>
<object>CustomFormPart__c</object>
<connector>
<targetReference>Loop_Parts</targetReference> <!-- loops back -->
</connector>
<inputAssignments>
<field>PartName__c</field>
<value>
<elementReference>Loop_Parts.Row_PartName.value</elementReference>
<!-- ↑ LOOP name + nested field name + .value -->
</value>
</inputAssignments>
<inputAssignments>
<field>Quantity__c</field>
<value>
<elementReference>Loop_Parts.Row_PartQty.value</elementReference>
</value>
</inputAssignments>
<storeOutputAutomatically>true</storeOutputAutomatically>
</recordCreates>
Don'ts:
<collectionReference>MyRepeater</collectionReference>→Element "MyRepeater" doesn't exist<collectionReference>MyRepeater.items</collectionReference>→ generic server error<collectionReference>MyRepeater.data</collectionReference>→ generic server error- Inside the loop body,
MyRepeater.Row_PartName.valuewon't work — must use the loop's name, not the repeater's name.
Cross-row validation does NOT compile. Formula refs like r_GR4.AllItems[$Items].field.value and r_GR4.AllItems[$Items - 1].field.value fail with Syntax error. Per-row validation works via plain fieldName.value inside the nested field's own validationRule. For cross-row rules, use a post-screen loops + decisions.
Other Repeater accessors don't resolve today. Only .AllItems works. AddedItems, PrepopulatedItems, RemovedItems all fail deploy with doesn't exist.
Prepopulating a Repeater from an existing collection
Bind an existing SObject collection to the Repeater so it renders one pre-filled row per source record. The user can then edit, add, or remove rows before submit.
Pattern: recordLookups (get source collection, getFirstRecordOnly=false, storeOutputAutomatically=true) → screen with Repeater bound via the collection inputParameter → nested fields use SourceCollection[$EachItem].FieldApiName as their value default.
<recordLookups>
<name>Get_Source</name>
<object>ServiceResource</object>
<getFirstRecordOnly>false</getFirstRecordOnly>
<storeOutputAutomatically>true</storeOutputAutomatically>
<connector><targetReference>Screen_Repeater</targetReference></connector>
<!-- optional <limit>, <filters> … -->
</recordLookups>
<!-- On the screen: -->
<fields>
<name>accountRepeater</name>
<fieldType>Repeater</fieldType>
<inputParameters>
<name>collection</name> <!-- ← binds source rows -->
<value><elementReference>Get_Source</elementReference></value>
</inputParameters>
<fields>
<name>account_info</name>
<fieldType>DisplayText</fieldType>
<fieldText><p>Id: {!Get_Source[$EachItem].Id}</p></fieldText>
<!-- DisplayText inside the Repeater merges via SourceCollection[$EachItem].Field -->
</fields>
<fields>
<name>name</name>
<extensionName>runtime_service_fieldservice:dcTextInput</extensionName>
<fieldType>ComponentInstance</fieldType>
<inputParameters>
<name>label</name>
<value><stringValue>Name</stringValue></value>
</inputParameters>
<inputParameters>
<name>value</name>
<value><elementReference>Get_Source[$EachItem].Name</elementReference></value>
<!-- ↑ prepopulates the editable field with the source record's value -->
</inputParameters>
<isRequired>true</isRequired>
<storeOutputAutomatically>true</storeOutputAutomatically>
<inputsOnNextNavToAssocScrn>UseStoredValues</inputsOnNextNavToAssocScrn>
<styleProperties>…</styleProperties>
</fields>
<isRequired>false</isRequired>
<styleProperties>…</styleProperties>
</fields>
Key points:
- The binding inputParameter is named
collection, notvalueorsource. - Inside the Repeater, reference a source row via
SourceCollectionName[$EachItem].FieldApiName— use the record-lookup's name, not the Repeater's name. Works in bothDisplayText.fieldText(as{!Get_Source[$EachItem].Id}) and in componentvaluedefaults (as<elementReference>Get_Source[$EachItem].Name</elementReference>). $EachItemis the per-row iterator Salesforce injects while rendering the Repeater. It only resolves inside Repeater-nested fields.- Downstream loops still iterate
Repeater_Name.AllItemsas normal — prepopulation changes the input, not the output accessor. - Prepopulated rows appear as regular
.AllItemsentries after submit; there is noPrepopulatedItems/AddedItemssplit (those accessors fail deploy).
Displaying Repeater entries to the user (post-Repeater Loop screen)
To show the user what they captured (e.g. review / confirmation / per-row detail), put a Loop after the Repeater screen whose body connects to a display screen; the display screen then connects back to the Loop. The end connector of the Loop moves on to the next step.
<loops>
<name>Loop_Through_Repeater</name>
<collectionReference>accountRepeater.AllItems</collectionReference>
<iterationOrder>Asc</iterationOrder>
<nextValueConnector>
<targetReference>Repeater_Output_Screen</targetReference> <!-- body = display screen -->
</nextValueConnector>
<!-- <noMoreValuesConnector> → next step after the review is done -->
</loops>
<screens>
<name>Repeater_Output_Screen</name>
<connector><targetReference>Loop_Through_Repeater</targetReference></connector> <!-- back to loop -->
<fields>
<name>display_info</name>
<fieldType>DisplayText</fieldType>
<fieldText><p>Source Id: {!Loop_Through_Repeater.UniqueField__Id}</p>
<p>Name: {!Loop_Through_Repeater.name.value}</p>
<p>Type: {!Loop_Through_Repeater.description.value}</p></fieldText>
<styleProperties>…</styleProperties>
</fields>
<allowFinish>true</allowFinish>
<showFooter>true</showFooter>
<nextOrFinishButtonLabel>Next</nextOrFinishButtonLabel>
</screens>
Accessor rules inside the loop body:
- User-captured values —
{!LoopName.nestedFieldName.value}(same.value/.selectedChoiceValues/.isActive/ etc. accessors as elsewhere). - Source record Id for prepopulated rows —
{!LoopName.UniqueField__Id}. This is a synthetic field the Repeater exposes on each iteration; it only carries a value for rows that came from the boundcollection(new rows the user added will be blank). - Use the loop's name, not the Repeater's name, inside the loop body — same rule as the canonical
.AllItems+ Create pattern above.
This loop-over-.AllItems display pattern composes with the Create/Update patterns: one loop for rendering a review screen, a later loop (or the same one, if ordering permits) for CUD. Remember the CUD rule — nothing (screens, gets, decisions) may sit between sequential CUD nodes, so any review loop must fully complete before the CUD chain starts.
CUD rules (hard platform constraints)
- A Decision can choose which CUD chain starts (e.g. Create vs Update branches of a save-mode decision). But once a CUD chain begins, no Decision may appear between sequential CUD nodes — deploy fails with
Append multiple Create, Update, or Delete operations only at the end of the flow, in any order.- Workaround for "create only if filled" → always-create (accept blank rows), or move the conditional logic before the CUD chain begins.
- All CUDs at end of flow. No Get Records or screens after any CUD. No subflows containing CUD.
- Assignment-after-CUD inside a loop was bugged in v260. Fixed in v262 / API 66. Safe to use now.
Counter params
<inputParameters><name>min</name><value><numberValue>1.0</numberValue></value></inputParameters>
<inputParameters><name>max</name><value><numberValue>10.0</numberValue></value></inputParameters>
<inputParameters><name>step</name><value><numberValue>1.0</numberValue></value></inputParameters>
<inputParameters><name>minCustomErrorMessage</name><value><stringValue>…</stringValue></value></inputParameters>
<inputParameters><name>maxCustomErrorMessage</name><value><stringValue>…</stringValue></value></inputParameters>
Date params
<inputParameters><name>minDate</name><value><elementReference>$Flow.CurrentDate</elementReference></value></inputParameters>
<inputParameters><name>maxDate</name><value><dateValue>2027-12-31</dateValue></value></inputParameters>
Picklist compact mode
Only use isCompact=true when ALL labels ≤8 characters AND ≤5 options.
Lookup params
<inputParameters><name>objectApiName</name><value><stringValue>Asset</stringValue></value></inputParameters>
<inputParameters><name>searchedFields</name><value><stringValue>Name, SerialNumber</stringValue></value></inputParameters>
<inputParameters><name>isMultiSelection</name><value><booleanValue>true</booleanValue></value></inputParameters>
<inputParameters><name>recordIdCollection</name><value><elementReference>v_Ids</elementReference></value></inputParameters>
recordIdCollection — scoping the searchable set
- Builder label: Record IDs Collection. XML attribute:
recordIdCollection(singularrecordId+Collectionsuffix).recordIdsfails deploy withWe can't find this input attribute: "recordIds". - It is a scoping filter, not a default pre-selection. Constrains the lookup to only search within the provided String collection of Ids.
- Only takes effect when
isMultiSelection=true. In single-select mode it is silently ignored — the user sees the full unfiltered object. - Canonical pattern:
recordLookups(scoped subset) →loops+assignments(build String collection of Ids) → screen withdcLookup recordIdCollection=v_Ids. Works offline against Briefcase-primed data; target < 1s over ~60k records. - Output in multi-select mode is
{!Lookup.recordIds}(String collection); visibility rules and DML that previously used{!Lookup.recordId}(singular) must iterate the collection or take the first element.
dcLookup displayed label
dcLookup has no input parameter for the displayed field (no displayField/primaryField). The label in search results and the selected chip is driven by the object's Primary Compact Layout — first field in that layout wins. To change it: Setup → Object Manager → Object → Compact Layouts → reorder → assign as Primary (org-wide change). searchedFields controls matching, not display.
Flow-local alternative: swap dcLookup for dcPicklist backed by a dynamicChoiceSets with <displayField> / <valueField>.
Address with GPS
<inputParameters><name>useCoordinates</name><value><booleanValue>true</booleanValue></value></inputParameters>
Matrix
<inputParameters><name>questions</name><value><stringValue>["Q1","Q2","Q3"]</stringValue></value></inputParameters>
Escape & as & inside the JSON string.
2-column section
<fields>
<name>MySection</name>
<fieldText>Section Header</fieldText>
<fieldType>RegionContainer</fieldType>
<fields>
<name>MySection_Col1</name>
<fieldType>Region</fieldType>
<fields><!-- components here --></fields>
<inputParameters><name>width</name><value><stringValue>6</stringValue></value></inputParameters>
<isRequired>false</isRequired>
</fields>
<fields>
<name>MySection_Col2</name>
<fieldType>Region</fieldType>
<fields><!-- components here --></fields>
<inputParameters><name>width</name><value><stringValue>6</stringValue></value></inputParameters>
<isRequired>false</isRequired>
</fields>
<isRequired>false</isRequired>
<regionContainerType>SectionWithHeader</regionContainerType>
…styleProperties…
</fields>
Visibility rule
<visibilityRule>
<conditionLogic>and</conditionLogic>
<conditions>
<leftValueReference>componentName.value</leftValueReference>
<operator>GreaterThan</operator>
<rightValue><numberValue>0.0</numberValue></rightValue>
</conditions>
</visibilityRule>
Property accessors: .value (input components, including dcCheckbox), .selectedChoiceValues (choice components), .isActive (toggle), .firstName / .lastName (Name component), .recordId / .recordIds (Lookup single / multi).
The same accessors are also used inside recordCreates / recordUpdates inputAssignments — e.g. <elementReference>new_Reading.value</elementReference>.
Conditionally-hidden required fields — use validationRule, not isRequired
IF(TriggerField.selectedChoiceValues = "Yes",
AND(NOT(ISBLANK(value)), value >= 0, value <= 100000),
TRUE)
Canonical Decision IsNull pattern
IsNull takes a booleanValue on the right, NOT a null literal:
<conditions>
<leftValueReference>v_ParentId</leftValueReference>
<operator>IsNull</operator>
<rightValue>
<booleanValue>false</booleanValue> <!-- true = is null, false = is not null -->
</rightValue>
</conditions>
Calculation timing (CRITICAL)
Calculated values cannot display on the same screen that collects the inputs — calculations run only after the user taps Next. Pattern:
Screen N (collect inputs) → recordLookups / assignments / decisions → Screen N+1 (display results)
Screen N's connector must point to the calculation element, NOT to Screen N+1. All decision branches must eventually converge on Screen N+1.
DisplayText formula limitations (mobile runtime)
DisplayText in DataCaptureFlow has severely limited formula support compared to standard flows — complex formulas deploy fine and preview in Builder but fail at runtime with Error while resolving default value reference.
Fails at runtime:
IF(Toggle.isActive, "YES", "NO")— any IF/CASE on component propertiesTEXT(CASE(...)),ADDMONTHS(...), nested date math- Mixing multiple component refs + formulas in one
fieldText
Works:
- Simple single variable:
{!var_RiskScore} - Simple component ref:
{!MyPicklist.selectedChoiceValues},{!MyNumeric.value} - Global vars:
{!$Flow.CurrentDate},{!$User.FirstName}
Pattern: pre-calculate in an <assignments> element → store in a variable → reference that variable in DisplayText.
Global variables as input value defaults
| Default binding | Variable | Works? |
|---|---|---|
dcTextInput | $User.Username | ✅ |
dcTextInput | $User.Name | ❌ type mismatch error |
dcDateTime | $Flow.InterviewStartTime | ✅ |
dcDate | $Flow.CurrentDate | ✅ |
Record Choice Set (dynamicChoiceSets) — mobile offline gotcha
<outputAssignments> inside a dynamicChoiceSets deploys fine but does NOT reliably populate the target variables at runtime in mobile offline DataCaptureFlow. Downstream screens render blank.
Correct pattern: keep the choice set minimal (displayField, valueField, filters, object, dataType). After the selection screen, route through a recordLookups filtered by Id = {!picklistName.selectedChoiceValues} and put extra fields into variables via the lookup's outputAssignments. Fits calculation-timing rule naturally (lookup sits between selection screen and display screen).
<recordLookups>
<name>gr_SelectedChild</name>
<filters>
<field>Id</field>
<operator>EqualTo</operator>
<value><elementReference>pl_Child.selectedChoiceValues</elementReference></value>
</filters>
<getFirstRecordOnly>true</getFirstRecordOnly>
<object>Child__c</object>
<outputAssignments>
<assignToReference>v_SelectedValue</assignToReference>
<field>Reading_Value__c</field>
</outputAssignments>
</recordLookups>
Visual polish patterns (verified render in mobile runtime)
Colors from the Lightning Design System: #2E844A (green), #0176D3 (blue), #FE9339 (orange), #C9C7C5 (neutral).
Hero banner
<div style="background: linear-gradient(135deg, #2E844A 0%, #0176D3 100%); color: white; padding: 20px; border-radius: 10px; text-align: center;">
<p style="margin: 0; font-size: 22px;"><strong>🔧 Site Visit Report</strong></p>
<p style="margin: 4px 0 0 0; font-size: 13px;">Subtitle</p>
</div>
Shortened here. Read the whole file on GitHub.
Signals
- GitHub stars
- 1k
- Forks
- 342
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
field-service-data-capture-reference-configure- Source
- github.com/forcedotcom/sf-skills
github.com/forcedotcom/sf-skills