14 KiB
OmniStudio Dependency Patterns
Overview
OmniStudio components form a directed graph where each component type can reference others. Dependencies are not stored in lookup fields — they are embedded in JSON configuration fields (PropertySetConfig, Definition, InputObjectName/OutputObjectName). Extracting dependencies requires parsing these JSON structures.
Dependency Direction Summary
OmniScript ──→ Integration Procedure (via IP Action element)
OmniScript ──→ Data Mapper (via DataRaptor Action element)
OmniScript ──→ OmniScript (via embedded OmniScript element)
OmniScript ──→ Apex Class (via Remote Action element)
OmniScript ──→ LWC (via Custom Lightning Web Component element)
OmniScript ──→ HTTP Endpoint (via HTTP Action element)
Integration Procedure ──→ Data Mapper (via DataRaptor Action element)
Integration Procedure ──→ Apex Class (via Remote Action element)
Integration Procedure ──→ HTTP Endpoint (via HTTP Action element)
Integration Procedure ──→ Integration Procedure (via nested IP Action element)
Integration Procedure ──→ OmniScript (via OmniScript Action element — uncommon)
FlexCard ──→ Integration Procedure (via data source configuration)
FlexCard ──→ Apex Class (via Apex data source)
FlexCard ──→ FlexCard (via child card reference)
FlexCard ──→ OmniScript (via action configuration — launches OS)
Data Mapper ──→ Salesforce Object (via InputObjectName — read)
Data Mapper ──→ Salesforce Object (via OutputObjectName — write)
OmniScript Dependencies
Element Types and Their Dependency Targets
OmniScript elements are stored as OmniProcessElement records (Core) or Element__c records (Vlocity). Each element has a PropertySetConfig / PropertySet__c JSON field containing the configuration.
DataRaptor Transform Action
Calls a Data Mapper to extract, transform, or load data.
PropertySetConfig structure:
{
"Type": "DataRaptor Transform Action",
"PropertySet": {
"bundle": "AccountExtract",
"bundleName": "AccountExtract",
"dataRaptorType": "Extract"
}
}
Extraction rule: PropertySet.bundle or PropertySet.bundleName → resolves to an OmniDataTransform record by Name.
DataRaptor Turbo Action
High-performance variant of DataRaptor Transform Action. Same JSON structure, same extraction rule.
PropertySetConfig structure:
{
"Type": "DataRaptor Turbo Action",
"PropertySet": {
"bundle": "AccountTurboExtract",
"bundleName": "AccountTurboExtract"
}
}
Integration Procedure Action
Calls an Integration Procedure.
PropertySetConfig structure:
{
"Type": "Integration Procedure Action",
"PropertySet": {
"integrationProcedureKey": "TypeName_SubTypeName",
"ipMethod": "TypeName",
"ipType": "SubTypeName",
"integrationProcedureVersion": 1
}
}
Extraction rule: PropertySet.integrationProcedureKey → resolves to an OmniProcess record where Type_SubType matches and TypeCategory = 'IntegrationProcedure'.
OmniScript Action (Embedded OmniScript)
Embeds or launches another OmniScript.
PropertySetConfig structure:
{
"Type": "OmniScript",
"PropertySet": {
"Type": "ChildScriptType",
"Sub Type": "ChildScriptSubType",
"Language": "English"
}
}
Extraction rule: PropertySet.Type + PropertySet["Sub Type"] + PropertySet.Language → resolves to an OmniProcess where TypeCategory = 'OmniScript' and fields match.
Remote Action
Calls an Apex class method.
PropertySetConfig structure:
{
"Type": "Remote Action",
"PropertySet": {
"remoteClass": "MyApexClassName",
"remoteMethod": "myMethodName",
"remoteTimeout": 30000
}
}
Extraction rule: PropertySet.remoteClass → Apex class name. PropertySet.remoteMethod → method name. Dependency is remoteClass.remoteMethod.
HTTP Action
Calls an external HTTP endpoint.
PropertySetConfig structure:
{
"Type": "HTTP Action",
"PropertySet": {
"httpUrl": "{externalEndpointUrl}",
"httpMethod": "POST",
"namedCredential": "MyNamedCredential"
}
}
Extraction rule: PropertySet.httpUrl or PropertySet.namedCredential → external dependency. If namedCredential is present, it references a Named Credential record.
Custom Lightning Web Component
Embeds an LWC inside the OmniScript.
PropertySetConfig structure:
{
"Type": "Custom Lightning Web Component",
"PropertySet": {
"lwcName": "myCustomComponent",
"lwcComponentName": "c-my-custom-component"
}
}
Extraction rule: PropertySet.lwcName or PropertySet.lwcComponentName → LWC component reference.
DocuSign Envelope Action
Triggers a DocuSign envelope.
PropertySetConfig structure:
{
"Type": "DocuSign Envelope Action",
"PropertySet": {
"docuSignTemplateId": "template-uuid"
}
}
Extraction rule: PropertySet.docuSignTemplateId → DocuSign template (external dependency).
Integration Procedure Dependencies
Integration Procedures use the same element types as OmniScripts but are filtered by TypeCategory = 'IntegrationProcedure' (Core) or IsIntegrationProcedure__c = true (Vlocity).
Element Types Available in IPs
| Element Type | Available in IP | Dependency Target |
|---|---|---|
| DataRaptor Transform Action | Yes | Data Mapper |
| DataRaptor Turbo Action | Yes | Data Mapper |
| Remote Action | Yes | Apex Class |
| HTTP Action | Yes | External endpoint |
| Integration Procedure Action | Yes (nested) | Another IP |
| Matrix Action | Yes | Calculation Matrix |
| Set Values | Yes | None (internal) |
| Conditional Block | Yes | None (internal) |
| Loop Block | Yes | None (internal) |
| Response Action | Yes | None (internal) |
| List Action | Yes | None (internal) |
Nested IP Pattern
An Integration Procedure can call another Integration Procedure, creating a chain:
IP: OrderValidation
└── IP Action → IP: CustomerLookup
└── DR Action → DM: CustomerExtract
└── IP Action → IP: InventoryCheck
└── HTTP Action → External inventory API
└── DR Action → DM: OrderTransform
FlexCard Dependencies
FlexCards store their entire configuration in the Definition JSON field on OmniUiCard (Core) or VlocityUITemplate__c (Vlocity).
Data Source Parsing
The Definition JSON contains a dataSources array:
{
"dataSources": [
{
"name": "AccountData",
"type": "IntegrationProcedure",
"value": {
"key": "fetchAccountData",
"inputMap": { "AccountId": "{recordId}" }
}
},
{
"name": "DirectApex",
"type": "Apex",
"value": {
"className": "AccountSummaryController",
"methodName": "getSummary"
}
},
{
"name": "SObjectData",
"type": "SObject",
"value": {
"sObjectType": "Account",
"fields": ["Name", "Industry", "Phone"]
}
}
]
}
Extraction rules by data source type:
| Data Source Type | JSON Path | Dependency Target |
|---|---|---|
IntegrationProcedure |
value.key |
Integration Procedure (Type_SubType key) |
Apex |
value.className |
Apex Class |
SObject |
value.sObjectType |
Salesforce Object (direct SOQL) |
DataRaptor |
value.bundle |
Data Mapper |
Child Card References
FlexCards can embed other FlexCards:
{
"children": [
{
"cardName": "ContactListCard",
"cardType": "childCard"
}
]
}
Extraction rule: children[].cardName → resolves to another OmniUiCard by Name.
Action References
FlexCard actions can launch OmniScripts:
{
"actions": [
{
"actionType": "OmniScript",
"actionValue": {
"type": "editAccount",
"subType": "step1",
"language": "English"
}
}
]
}
Extraction rule: actions[].actionValue.type + subType + language → resolves to an OmniScript.
Data Mapper Dependencies
Data Mappers (DataRaptors) reference Salesforce objects through their items.
Object References via Items
Each OmniDataTransformItem (Core) or DRMapItem__c (Vlocity) record contains:
| Field | Purpose | Dependency Type |
|---|---|---|
InputObjectName / InterfaceObject__c |
Source sObject for reads | sObject (read access) |
OutputObjectName / TargetFieldObjectType__c |
Target sObject for writes | sObject (write access) |
InputFieldName / InterfaceFieldAPIName__c |
Source field | Field-level dependency |
OutputFieldName / TargetFieldAPIName__c |
Target field | Field-level dependency |
Extract Type Data Mapper Example
DM: AccountExtract (Type: Extract)
├── Item 1: InputObjectName = "Account"
│ InputFieldName = "Name"
│ OutputFieldName = "AccountName"
├── Item 2: InputObjectName = "Account"
│ InputFieldName = "Industry"
│ OutputFieldName = "AccountIndustry"
└── Item 3: InputObjectName = "Contact"
InputFieldName = "Email"
OutputFieldName = "PrimaryEmail"
Dependencies: Account (read), Contact (read)
Load Type Data Mapper Example
DM: OrderCreate (Type: Load)
├── Item 1: OutputObjectName = "Order"
│ InputFieldName = "OrderData.accountId"
│ OutputFieldName = "AccountId"
└── Item 2: OutputObjectName = "OrderItem"
InputFieldName = "OrderData.lineItems[].productId"
OutputFieldName = "Product2Id"
Dependencies: Order (write), OrderItem (write)
Transform Type Data Mapper
Transform type Data Mappers do not reference Salesforce objects directly — they map between data structures. They have no sObject dependencies but may participate in a chain:
OmniScript → DR Extract (reads Account) → DR Transform (reshapes data) → DR Load (writes CustomObj__c)
Circular Dependency Detection
Why Circular Dependencies Occur
Circular references happen when component A depends on component B, which directly or transitively depends back on component A. Common scenarios:
- OmniScript ↔ IP: OmniScript calls IP via IP Action, IP calls back to OmniScript via OmniScript Action
- IP ↔ IP: IP A calls IP B via nested IP Action, IP B calls IP A
- FlexCard → IP → OmniScript → FlexCard: FlexCard sources data from IP, IP triggers OmniScript, OmniScript launches FlexCard
Detection Algorithm
function detectCircularDependencies(graph):
cycles = []
for each node N in graph:
visited = empty set
path = empty list
dfs(N, visited, path, graph, cycles)
return cycles
function dfs(node, visited, path, graph, cycles):
if node is in path:
// Circular reference found
cycleStart = index of node in path
cycle = path[cycleStart:] + [node]
cycles.append(cycle)
return
if node is in visited:
return
visited.add(node)
path.append(node)
for each neighbor of node in graph:
dfs(neighbor, visited, path, graph, cycles)
path.removeLast()
Reporting Circular References
When a cycle is detected, report it clearly:
CIRCULAR DEPENDENCY DETECTED:
OS:editAccount → IP:validateAccount → OS:editAccount
Components in cycle:
1. OmniScript "editAccount" (IP Action → validateAccount)
2. Integration Procedure "validateAccount" (OmniScript Action → editAccount)
Risk: Runtime infinite loop if not guarded by conditional logic.
Recommendation: Review whether the back-reference is intentional and has
a termination condition.
Dependency Graph Construction
Step-by-Step Process
1. DETECT namespace (see namespace-guide.md)
2. QUERY all container objects:
- OmniProcess (OmniScripts + IPs)
- OmniUiCard (FlexCards)
- OmniDataTransform (Data Mappers)
3. QUERY all element objects:
- OmniProcessElement (for each OmniProcess)
- OmniDataTransformItem (for each OmniDataTransform)
4. PARSE each element's PropertySetConfig:
- Identify element Type
- Extract dependency reference per extraction rules above
- Resolve reference to a known component record
5. PARSE each FlexCard's Definition:
- Extract dataSources array
- Extract children array
- Extract actions array
- Resolve references to known components
6. PARSE each Data Mapper's items:
- Extract InputObjectName / OutputObjectName
- Resolve to sObject names
7. BUILD directed graph:
- Nodes = all components + referenced sObjects + external endpoints
- Edges = dependency references with type labels
8. DETECT circular references:
- Run DFS cycle detection
- Record all cycles found
9. COMPUTE impact analysis:
- For each node, compute transitive closure of inbound edges
- "If X changes, these components are affected"
Impact Analysis: Reverse Dependency Lookup
To answer "what breaks if I change Data Mapper X?", reverse the dependency direction:
Given: DM:AccountExtract
Direct dependents (components that reference this DM):
→ IP:fetchAccountData (DataRaptor Action)
Transitive dependents (components that reference the direct dependents):
→ OS:updateAccount (IP Action → fetchAccountData)
→ FC:AccountSummaryCard (Data Source → fetchAccountData)
Full impact set: [IP:fetchAccountData, OS:updateAccount, FC:AccountSummaryCard]
Property Set Config Parsing Tips
Handling Large JSON
PropertySetConfig can exceed 100KB for complex elements. When querying via SOQL:
- SOQL
SELECTreturns the full field value - For very large configs, the Tooling API may be necessary
- Parse incrementally if memory is a concern
Nested Property Sets
Some elements have nested structures. Always check for:
PropertySet.bundle(top-level reference)PropertySet.elementProperties(per-field configs)PropertySet.conditionalProperties(conditional logic)PropertySet.remoteOptions(additional remote action config)
Common Pitfalls
| Pitfall | Handling |
|---|---|
bundleName vs bundle |
Both may exist; prefer bundle as the canonical reference |
integrationProcedureKey format |
Always Type_SubType with underscore separator |
| Version-specific references | Some elements reference a specific version; default is latest active |
| Null PropertySetConfig | Skip elements with null/empty config — they have no dependencies |
| JSON parsing errors | Malformed JSON in PropertySetConfig can occur on manually edited records; catch and log |