
Building Omnistudio Integration Procedure
- 1.9k installs
- 763 repo stars
- Updated July 24, 2026
- forcedotcom/sf-skills
building-omnistudio-integration-procedure is an agent skill that OmniStudio Integration Procedure creation and validation with 110-point scoring. Use this skill when building server-sid.
About
building omnistudio integration procedure OmniStudio Integration Procedure Creation and Validation Expert OmniStudio Integration Procedure IP builder with deep knowledge of server side process orchestration Create production ready IPs that combine DataRaptor Data Mapper actions Apex Remote Actions HTTP callouts conditional logic and nested procedure calls into declarative multi step operations In scope Creating well structured Integration Procedures from requirements selecting and wiring element types DataRaptor Remote Action HTTP Conditional Block Loop Set Values nested IP dependency validation error handling patterns 110 point scoring deployment and activation Out of scope Building OmniScripts use building omnistudio omniscript creating Data Mappers directly use building omnistudio datamapper designing FlexCards use building omnistudio flexcard mapping full dependency trees use analyzing omnistudio dependencies deploying metadata to org use deploying metadata Purpose What business process is this IP orchestrating e g onboard a new account process an order Target objects data sources Which Salesforce objects external APIs or both Type SubType naming PascalCase pair that uniquely.
- name: building-omnistudio-integration-procedure
- description: "OmniStudio Integration Procedure creation and validation with 110-point scoring. Use this skill when build
- Expert OmniStudio Integration Procedure (IP) builder with deep knowledge of server-side process orchestration. Create pr
- Follow building-omnistudio-integration-procedure SKILL.md steps and documented constraints.
- Follow building-omnistudio-integration-procedure SKILL.md steps and documented constraints.
Building Omnistudio Integration Procedure by the numbers
- 1,931 all-time installs (skills.sh)
- +6 installs in the week ending Jul 28, 2026 (Skillselion tracking)
- Ranked #628 of 16,659 AI & Agent Building skills by installs in the Skillselion catalog
- Security screen: MEDIUM risk (skills.sh audit)
- Data as of Jul 28, 2026 (Skillselion catalog sync)
building-omnistudio-integration-procedure capabilities & compatibility
- Capabilities
- name: building omnistudio integration procedure · description: "omnistudio integration procedure c · expert omnistudio integration procedure (ip) bui · follow building omnistudio integration procedure
- Use cases
- orchestration
What building-omnistudio-integration-procedure says it does
name: building-omnistudio-integration-procedure
description: "OmniStudio Integration Procedure creation and validation with 110-point scoring. Use this skill when building server-side process orchestrations that combine Data Mapper actions, Apex Re
Expert OmniStudio Integration Procedure (IP) builder with deep knowledge of server-side process orchestration. Create production-ready IPs that combine DataRaptor/Data Mapper actions, Apex Remote Acti
npx skills add https://github.com/forcedotcom/sf-skills --skill building-omnistudio-integration-procedureAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 1.9k |
|---|---|
| repo stars | ★ 763 |
| Security audit | 2 / 3 scanners passed |
| Last updated | July 24, 2026 |
| Repository | forcedotcom/sf-skills ↗ |
When should an agent use building-omnistudio-integration-procedure and what problem does it solve?
OmniStudio Integration Procedure creation and validation with 110-point scoring. Use this skill when building server-side process orchestrations that combine Data Mapper actions, Apex Remote Actions,
Who is it for?
Developers invoking building-omnistudio-integration-procedure as documented in the skill source.
Skip if: Skip when requirements fall outside building-omnistudio-integration-procedure documented scope.
When should I use this skill?
OmniStudio Integration Procedure creation and validation with 110-point scoring. Use this skill when building server-side process orchestrations that combine Data Mapper actions, Apex Remote Actions,
What you get
Outputs aligned with the building-omnistudio-integration-procedure SKILL.md workflow and stated deliverables.
- OmniStudio element JSON templates
- PropertySetConfig blocks
Files
building-omnistudio-integration-procedure: OmniStudio Integration Procedure Creation and Validation
Expert OmniStudio Integration Procedure (IP) builder with deep knowledge of server-side process orchestration. Create production-ready IPs that combine DataRaptor/Data Mapper actions, Apex Remote Actions, HTTP callouts, conditional logic, and nested procedure calls into declarative multi-step operations.
Scope
- In scope: Creating well-structured Integration Procedures from requirements; selecting and wiring element types (DataRaptor, Remote Action, HTTP, Conditional Block, Loop, Set Values, nested IP); dependency validation; error handling patterns; 110-point scoring; deployment and activation
- Out of scope: Building OmniScripts (use
building-omnistudio-omniscript), creating Data Mappers directly (usebuilding-omnistudio-datamapper), designing FlexCards (usebuilding-omnistudio-flexcard), mapping full dependency trees (useanalyzing-omnistudio-dependencies), deploying metadata to org (usedeploying-metadata)
---
Required Inputs
- Purpose: What business process is this IP orchestrating? (e.g., "onboard a new account", "process an order")
- Target objects / data sources: Which Salesforce objects, external APIs, or both?
- Type / SubType naming: PascalCase pair that uniquely identifies the IP (e.g.,
Type=OrderProcessing,SubType=Standard) - Target org alias: Authenticated org alias for deployment (e.g.,
myDevOrg)
---
Quick Reference
Scoring: 110 points across 6 categories. Thresholds: ✅ 90+ (Deploy) | ⚠️ 67-89 (Review) | ❌ <67 (Block - fix required)
---
Core Responsibilities
1. IP Generation: Create well-structured Integration Procedures from requirements, selecting correct element types and wiring inputs/outputs 2. Element Composition: Assemble DataRaptor actions, Remote Actions, HTTP callouts, conditional blocks, loops, and nested IP calls into coherent orchestrations 3. Dependency Analysis: Validate that referenced DataRaptors, Apex classes, and nested IPs exist and are active before deployment 4. Error Handling: Enforce try/catch patterns, conditional rollback, and response validation across all data-modifying steps (DML — Data Manipulation Language)
---
CRITICAL: Orchestration Order
analyzing-omnistudio-dependencies -> building-omnistudio-datamapper -> building-omnistudio-integration-procedure -> building-omnistudio-omniscript -> building-omnistudio-flexcard (you are here: building-omnistudio-integration-procedure)
Data Mappers referenced by the IP must exist FIRST. Build and deploy DataRaptors/Data Mappers before the IP that calls them. The IP must be active before any OmniScript or FlexCard can invoke it.
---
Key Insights
| Insight | Details |
|---|---|
| Chaining | IPs call other IPs via Integration Procedure Action elements. Output of one step feeds input of the next via response mapping. Design data flow linearly where possible. |
| Response Mapping | Each element's output is namespaced under its element name in the response JSON. Use %elementName:keyPath% syntax to reference upstream outputs in downstream inputs. |
| Caching | IPs support platform cache for read-heavy orchestrations. Set cacheType and cacheTTL in the procedure's PropertySet. Avoid caching procedures that perform DML. |
| Versioning | Type/SubType pairs uniquely identify an IP. Use SubType for versioning (e.g., Type=AccountOnboarding, SubType=v2). Only one version can be active at a time per Type/SubType. |
Core Namespace Discriminator: OmniStudio Core stores both Integration Procedures and OmniScripts in the OmniProcess table. Use IsIntegrationProcedure = true or OmniProcessType = 'Integration Procedure' to filter IPs. Without a filter, queries return mixed results.
CRITICAL — Creating IPs via Data API: When creating OmniProcess records, setIsIntegrationProcedure = trueto make the record an Integration Procedure. TheOmniProcessTypepicklist is computed from this boolean and cannot be set directly. Also,Nameis a required field onOmniProcess(not documented in standard OmniStudio docs). Usesf api request rest --method POST --body @file.jsonfor creation — thesf data create record --valuesflag cannot handle JSON textarea fields likePropertySetConfig.
---
Workflow Design (5-Phase Pattern)
Phase 1: Requirements Gathering
Before building, evaluate alternatives: Sometimes a single DataRaptor, an Apex service, or a Flow is the better choice. IPs are optimal when you need declarative multi-step orchestration with branching, error handling, and mixed data sources.
Ask the user to gather:
- Purpose and business process being orchestrated
- Target objects and data sources (Salesforce objects, external APIs, or both)
- Type/SubType naming (e.g.,
Type=OrderProcessing,SubType=Standard) - Target org alias for deployment
Then: Check existing IPs via CLI query (see CLI Commands below), identify reusable DataRaptors/Data Mappers, and review dependent components with analyzing-omnistudio-dependencies.
Phase 2: Design & Element Selection
| Element Type | Use Case | PropertySet Key |
|---|---|---|
| DataRaptor Extract Action | Read Salesforce data | bundle |
| DataRaptor Load Action | Write Salesforce data | bundle |
| DataRaptor Transform Action | Data shaping/mapping | bundle |
| Remote Action | Call Apex class method | remoteClass, remoteMethod |
| Integration Procedure Action | Call nested IP | ipMethod (format: Type_SubType) |
| HTTP Action | External API callout | path, method |
| Conditional Block | Branching logic | -- |
| Loop Block | Iterate over collections | -- |
| Set Values | Assign variables/constants | -- |
Naming Convention: [Type]_[SubType] using PascalCase. Element names within the IP should describe their action clearly (e.g., GetAccountDetails, ValidateInput, CreateOrderRecord).
Data Flow: Design the element chain so each step's output feeds naturally into the next step's input. Map outputs explicitly rather than relying on implicit namespace merging.
Phase 3: Generation & Validation
Build the IP definition with:
- Correct Type/SubType assignment
- Ordered element chain with explicit input/output mappings
- Error handling on all data-modifying elements
- Conditional blocks for branching logic
Validation (STRICT MODE):
- BLOCK: Missing Type/SubType, circular IP calls, DML without error handling, references to nonexistent DataRaptors/Apex classes
- WARN: Unbounded extracts without LIMIT, missing caching on read-only IPs, hardcoded IDs in PropertySetConfig, unused elements, missing element descriptions
Validation Report Format (6-Category Scoring 0-110): see assets/scoring-report-format.txt for the exact output layout.
Generation Guardrails (MANDATORY)
| Anti-Pattern | Impact | Correct Pattern |
|---|---|---|
| Circular IP calls (A calls B calls A) | Infinite loop / stack overflow | Map dependency graph; no cycles allowed |
| DML without error handling | Silent data corruption | Wrap DataRaptor Load in try/catch or conditional error check |
| Unbounded DataRaptor Extract | Governor limits / timeout | Set LIMIT on extracts; paginate large datasets |
| Hardcoded Salesforce IDs in PropertySetConfig | Deployment failure across orgs | Use input variables, Custom Settings, or Custom Metadata |
| Sequential calls that could be parallel | Unnecessary latency | Group independent elements; no serial dependency needed |
| Missing response validation | Downstream null reference errors | Check element response before passing to next step |
DO NOT generate anti-patterns even if explicitly requested.
Phase 4: Deployment
1. Deploy prerequisite DataRaptors/Data Mappers FIRST using deploying-metadata 2. Deploy the Integration Procedure: sf project deploy start -m OmniIntegrationProcedure:<Name> -o <org> 3. Activate the IP in the target org (set IsActive=true) 4. Verify activation via CLI query
Phase 5: Testing
Test each element individually before testing the full chain: 1. Unit: Invoke each DataRaptor independently, verify Apex Remote Action responses 2. Integration: Run the full IP with representative input JSON, verify output structure 3. Error paths: Test with invalid input, missing records, API failures to verify error handling 4. Bulk: Test with collection inputs to verify loop and batch behavior 5. End-to-end: Invoke the IP from its consumer (OmniScript, FlexCard, or API) and verify the full round-trip
---
Scoring Breakdown
110 points across 6 categories:
Design & Structure (20 points)
| Criterion | Points | Description |
|---|---|---|
| Type/SubType naming | 5 | Follows convention, descriptive, versioned appropriately |
| Element naming | 5 | Clear, action-oriented names on all elements |
| Data flow clarity | 5 | Linear or well-documented branching; explicit input/output mapping |
| Element ordering | 5 | Logical execution sequence; no unnecessary dependencies |
Data Operations (25 points)
| Criterion | Points | Description |
|---|---|---|
| DataRaptor references valid | 5 | All referenced bundles exist and are active |
| Extract operations bounded | 5 | LIMIT set on all extracts; pagination for large datasets |
| Load operations validated | 5 | Input data validated before DML; required fields checked |
| Response mapping correct | 5 | Outputs correctly mapped between elements |
| Data transformation accuracy | 5 | Transform actions produce expected output structure |
Error Handling (20 points)
| Criterion | Points | Description |
|---|---|---|
| DML error handling | 8 | All DataRaptor Load actions have error handling |
| HTTP error handling | 4 | All HTTP actions check status codes and handle failures |
| Remote Action error handling | 4 | Apex exceptions caught and surfaced |
| Rollback strategy | 4 | Multi-step DML has conditional rollback or compensating actions |
Performance (20 points)
| Criterion | Points | Description |
|---|---|---|
| No unbounded queries | 5 | All extracts have reasonable LIMIT values |
| Caching applied | 5 | Read-only procedures use platform cache where appropriate |
| Parallel execution | 5 | Independent elements not serialized unnecessarily |
| No redundant calls | 5 | Same data not fetched multiple times across elements |
Security (15 points)
| Criterion | Points | Description |
|---|---|---|
| No hardcoded IDs | 5 | IDs passed as input variables or from metadata |
| No hardcoded credentials | 5 | API keys/tokens use Named Credentials or Custom Settings |
| Input validation | 5 | User-supplied input sanitized before use in queries or DML |
Documentation (10 points)
| Criterion | Points | Description |
|---|---|---|
| Procedure description | 3 | Clear description of purpose and business context |
| Element descriptions | 4 | Each element has a description explaining its role |
| Input/output documentation | 3 | Expected input JSON and output JSON structure documented |
---
CLI Commands
Read `scripts/cli-commands.sh` before querying or deploying Integration Procedures — it contains all SOQL queries and sf project deploy/retrieve commands ready to adapt.
Core Namespace Note: The IsIntegrationProcedure=true filter is REQUIRED (or equivalently OmniProcessType='Integration Procedure'). OmniScript and Integration Procedure records share the OmniProcess sObject. Without this filter, queries return both types and produce misleading results.
---
Cross-Skill Integration
| From Skill | To building-omnistudio-integration-procedure | When |
|---|---|---|
| analyzing-omnistudio-dependencies | -> building-omnistudio-integration-procedure | "Analyze dependencies before building IP" |
| building-omnistudio-datamapper | -> building-omnistudio-integration-procedure | "DataRaptor/Data Mapper is ready, wire it into IP" |
| generating-apex | -> building-omnistudio-integration-procedure | "Apex Remote Action class deployed, configure in IP" |
| From building-omnistudio-integration-procedure | To Skill | When |
|---|---|---|
| building-omnistudio-integration-procedure | -> deploying-metadata | "Deploy IP to target org" |
| building-omnistudio-integration-procedure | -> building-omnistudio-omniscript | "IP is active, build OmniScript that calls it" |
| building-omnistudio-integration-procedure | -> building-omnistudio-flexcard | "IP is active, build FlexCard data source" |
| building-omnistudio-integration-procedure | -> analyzing-omnistudio-dependencies | "Verify IP dependency graph before deployment" |
---
Edge Cases
| Scenario | Solution |
|---|---|
| IP calls itself (direct recursion) | Block at design time; circular dependency check is mandatory |
| IP calls IP that calls original (indirect recursion) | Map full call graph; analyzing-omnistudio-dependencies detects cycles |
| DataRaptor not yet deployed | Deploy DataRaptors first; IP deployment will fail on missing references |
| External API timeout | Set timeout values on HTTP Action elements; implement retry logic or graceful degradation |
| Large collection input to Loop Block | Set batch size; test with realistic data volumes to avoid CPU timeout |
| Type/SubType collision with existing IP | Query existing IPs before creating; SubType versioning avoids collisions |
| Mixed namespace (Vlocity vs Core) | Confirm org namespace; element property names differ between packages |
Debug: IP not executing -> check IsActive flag + Type/SubType match | Elements skipped -> verify conditional block logic + input data shape | Timeout -> check DataRaptor query scope + HTTP timeout settings | Deployment failure -> verify all referenced components deployed and active
---
Output Expectations
Deliverables produced by this skill:
- Integration Procedure JSON (
assets/omni-process-ip.jsontemplate) —OmniProcessrecord ready for REST API creation withIsIntegrationProcedure=true - Element JSON records (
assets/omni-process-element-dr-extract.json,assets/omni-process-element-set-values.jsontemplates) —OmniProcessElementrecords for each action step withPropertySetConfigwired - Validation report — 110-point score across 6 categories with deploy/review/block threshold result
- Deployment checklist — confirms prerequisite DataRaptors are active, IP is activated, and consuming OmniScript or FlexCard can invoke it
---
Notes
API: Latest (check current Salesforce release notes; was 66.0 at time of authoring) | Mode: Strict (warnings block) | Scoring: Block deployment if score < 67
Dependencies (optional): deploying-metadata, building-omnistudio-datamapper, analyzing-omnistudio-dependencies
Creating IPs programmatically: Use REST API (sf api request rest --method POST --body @file.json). Required fields: Name, Type, SubType, Language, VersionNumber, IsIntegrationProcedure=true. Then create OmniProcessElement child records for each action step (also via REST API for JSON PropertySetConfig). Activate by setting IsActive=true after all elements are created.
---
Reference File Index
| File | When to read |
|---|---|
assets/omni-process-ip.json | Phase 3 — Generation: use as the OmniProcess record template when creating the Integration Procedure via REST API |
assets/omni-process-element-dr-extract.json | Phase 3 — Generation: use as the DataRaptor Extract Action element template; adapt for other DR action types |
assets/omni-process-element-set-values.json | Phase 3 — Generation: use as the Set Values element template for variable assignment steps |
assets/scoring-report-format.txt | Phase 3 — Validation: use as the output layout template when presenting the 110-point validation report |
references/best-practices.md | Phase 2-5 — Design patterns: element composition, error handling, caching, parallel execution, and security guidance |
references/element-types.md | Phase 2 — Element selection: read before configuring PropertySetConfig for any element type |
scripts/cli-commands.sh | Phase 1 & 4 — CLI queries and deploy/retrieve commands; adapt by replacing <Name> and <org> placeholders |
{
"OmniProcessId": "{{ParentProcessId}}",
"Name": "{{ElementName}}",
"Type": "DataRaptor Extract Action",
"Description": "{{Description}}",
"IsActive": true,
"Level": 0,
"SequenceNumber": {{SequenceNumber}},
"PropertySetConfig": "{\"bundle\":\"{{DataRaptorName}}\",\"chainOnStep\":false,\"disableUniqueSFDCObjectCheck\":false,\"failOnStepError\":true,\"ignoreCache\":false,\"postTransformBundle\":\"\",\"preTransformBundle\":\"\",\"remoteOptions\":{},\"responseJSONNode\":\"\",\"responseJSONPath\":\"\",\"sendOnlyFailedResponse\":false,\"vlcSI\":{\"bundleName\":\"\",\"interfaceName\":\"\",\"remoteMethodName\":\"\"}}"
}
{
"OmniProcessId": "{{ParentProcessId}}",
"Name": "{{ElementName}}",
"Type": "Set Values",
"Description": "{{Description}}",
"IsActive": true,
"Level": 0,
"SequenceNumber": {{SequenceNumber}},
"PropertySetConfig": "{\"ElementValueMap\":{\"{{OutputKey}}\":\"{{OutputValue}}\"},\"chainOnStep\":false,\"failOnStepError\":true}"
}
{
"Name": "{{Type}}_{{SubType}}",
"Type": "{{Type}}",
"SubType": "{{SubType}}",
"Language": "English",
"VersionNumber": 1,
"IsActive": false,
"IsIntegrationProcedure": true,
"Description": "{{Description}}",
"PropertySetConfig": "{\"persistentComponent\":true,\"trackingCustomData\":{},\"enableLWCRuntime\":true}",
"WebComponentKey": "{{Type}}/{{SubType}}/English/1"
}
Validation Report Format — 6-Category Scoring (0–110 points)
Score: 95/110 Very Good
|- Design & Structure: 18/20 (90%)
|- Data Operations: 23/25 (92%)
|- Error Handling: 18/20 (90%)
|- Performance: 18/20 (90%)
|- Security: 13/15 (87%)
|- Documentation: 5/10 (50%)
Thresholds:
90–110 ✅ Deploy
67–89 ⚠️ Review — address findings before deploying
<67 ❌ Block — fix required before any deployment
Credits & Acknowledgments
This skill was built upon the collective wisdom of the Salesforce OmniStudio community. We gratefully acknowledge the following resources whose ideas, patterns, and best practices have shaped this skill.
---
<!-- Parent: building-omnistudio-integration-procedure/SKILL.md -->
Integration Procedure Best Practices
Version: 1.0.0
Applies to: OmniStudio Integration Procedures (Core namespace and Vlocity managed package)
This guide consolidates best practices for building maintainable, performant, and reliable Integration Procedures.
---
Table of Contents
Strategy & Planning 1. When to Use an Integration Procedure 2. IP vs Flow vs Apex: Decision Framework
Design & Structure 3. Element Ordering and Execution Flow 4. Naming Conventions 5. Input/Output Contract Design
Error Handling 6. Error Handling Patterns 7. Conditional Rollback
Data Operations 8. Response Mapping from Data Mappers 9. Bounded Extracts and Pagination
Performance 10. Caching Strategies 11. Batch vs Sequential Execution 12. Reducing Redundant Calls
Maintenance 13. Versioning with Type/SubType 14. Documentation Standards
---
1. When to Use an Integration Procedure
Integration Procedures are optimal when:
- You need declarative multi-step orchestration combining reads, writes, transforms, and callouts
- The process involves multiple data sources (Salesforce objects + external APIs)
- You need server-side execution without user interaction (unlike OmniScripts)
- The orchestration requires conditional branching or loop iteration over collections
- You want reusability — the same IP can be called by OmniScripts, FlexCards, Apex, or other IPs
Integration Procedures are NOT optimal when:
- A single DataRaptor can accomplish the task (no orchestration needed)
- The logic is purely computational with no data access (use Apex or a Formula)
- You need real-time user interaction during execution (use OmniScript with embedded IP calls)
- The process is a simple CRUD operation on one object (use a DataRaptor Load directly)
---
2. IP vs Flow vs Apex: Decision Framework
| Criterion | Integration Procedure | Flow | Apex |
|---|---|---|---|
| Multi-step orchestration | Strong — designed for chaining | Possible but verbose | Manual coding required |
| External API callouts | HTTP Action element | HTTP Callout action (GA) | Full control via HttpRequest |
| OmniStudio integration | Native — called by OmniScript/FlexCard | Requires adapter | Requires @AuraEnabled or REST |
| Complex business logic | Limited — use Remote Action for complex logic | Decision elements | Full language support |
| Bulk data processing | Loop Block with batch sizing | Collection operations | SOQL/DML with governor awareness |
| Error handling granularity | Per-element try/catch | Fault paths per DML | Full try/catch/finally |
| Admin maintainability | Declarative, visual | Declarative, visual | Requires developer |
| Debugging | Preview panel, debug logs | Flow debug | Apex debug logs |
| Transaction control | Limited — each element may be its own transaction | Single transaction (before-save) or per-screen | Full transaction control |
Rule of thumb: If the process serves OmniStudio components, use an IP. If it serves standard Salesforce UI automation, use a Flow. If it requires complex computation or fine-grained transaction control, use Apex.
---
3. Element Ordering and Execution Flow
Elements execute top-to-bottom in the order they appear in the procedure definition. Follow these ordering principles:
Recommended Order
1. Set Values — Initialize variables, set defaults, normalize input 2. Validation — Conditional Block to validate required inputs before proceeding 3. Data Retrieval — DataRaptor Extract or HTTP Action to fetch required data 4. Data Transformation — DataRaptor Transform to shape data for downstream use 5. Business Logic — Conditional Blocks, Remote Actions for decisions 6. Data Modification — DataRaptor Load to write records 7. Response Assembly — Set Values to build the output response
Ordering Rules
- Fetch before transform: Extract data before attempting to reshape it
- Validate before mutate: Check preconditions before performing DML
- Independent reads first: Group all DataRaptor Extracts at the top where possible
- Error-prone steps last: Place steps that might fail (HTTP, DML) after all preparatory work
- Response assembly at the end: Build the final output after all processing completes
---
4. Naming Conventions
Type/SubType
- Type: Business process name in PascalCase (e.g.,
AccountOnboarding,OrderProcessing) - SubType: Version or variant identifier (e.g.,
Standard,v2,Retail) - Combined format:
Type_SubType(e.g.,AccountOnboarding_Standard)
Element Names
- Use descriptive, action-oriented names:
GetAccountDetails,ValidateAddress,CreateContactRecord - Prefix with the action type for scannability:
Extract_,Load_,Transform_,Check_,Call_ - Avoid generic names like
Step1,Action2,Process
Variables
- Input variables:
in_variableName - Output variables:
out_variableName - Internal variables:
var_variableName
---
5. Input/Output Contract Design
Define clear contracts for what the IP expects and returns:
Input Contract
{
"accountId": "001XXXXXXXXXXXX",
"includeContacts": true,
"contactLimit": 10
}- Document every expected input field with type and whether it is required
- Set default values in a Set Values element for optional inputs
- Validate required inputs with a Conditional Block before processing
Output Contract
{
"success": true,
"account": { "Name": "Acme Corp", "Industry": "Technology" },
"contacts": [ { "Name": "John Doe", "Email": "john@acme.com" } ],
"errors": []
}- Return a consistent structure regardless of success or failure
- Include a
successboolean and anerrorsarray in every response - Namespace outputs clearly so consumers know which element produced which data
---
6. Error Handling Patterns
Per-Element Error Handling
Every element that performs DML or makes an external call should have error handling:
1. DataRaptor Load Actions: Check the response for error indicators. Use a Conditional Block after the Load to inspect the result and branch to error handling if needed.
2. HTTP Actions: Check the HTTP status code in the response. Branch on non-2xx responses.
3. Remote Actions: Apex exceptions surface in the element response. Check for error keys in the output.
Try/Catch Pattern
Structure error handling as a three-step pattern:
1. Try: Execute the data-modifying element 2. Check: Conditional Block inspects the element's response for errors 3. Handle: Set Values element builds an error response or a Conditional Block routes to compensating actions
Error Response Structure
{
"success": false,
"errors": [
{
"element": "CreateOrderRecord",
"code": "FIELD_CUSTOM_VALIDATION_EXCEPTION",
"message": "Order amount exceeds credit limit"
}
]
}---
7. Conditional Rollback
When an IP performs multiple sequential DML operations and a later step fails:
Compensating Action Pattern
1. After each successful DML, store the created record IDs in variables 2. If a subsequent step fails, use stored IDs to execute compensating DataRaptor Load actions (delete/update) to undo prior changes 3. Return a clear error response indicating which steps succeeded and which failed
Design Considerations
- IPs do not have native transaction rollback across elements
- Each DataRaptor Load action may commit independently
- Plan for partial failure in multi-step write operations
- Consider whether the business process can tolerate partial completion
- For strict atomicity requirements, move the multi-DML logic to an Apex Remote Action where you control the transaction boundary
---
8. Response Mapping from Data Mappers
How Response Namespacing Works
Each element's output is stored under a key matching the element name. For example, if a DataRaptor Extract element named GetAccount returns { "Name": "Acme" }, the IP's response contains:
{
"GetAccount": {
"Name": "Acme"
}
}Referencing Upstream Outputs
In downstream elements, reference upstream data using the element namespace:
- In PropertySetConfig:
%GetAccount:Name% - In Set Values: Map from
GetAccount.Nameto the target variable
Common Mapping Issues
| Problem | Cause | Fix |
|---|---|---|
| Null value in downstream element | Element name mismatch in reference | Verify exact element name spelling and case |
| Array vs object confusion | DataRaptor returns collection, consumer expects single | Use [0] accessor or Transform to extract first item |
| Nested path not resolving | Deep path syntax incorrect | Use dot notation: GetAccount.BillingAddress.City |
---
9. Bounded Extracts and Pagination
Setting Limits
Every DataRaptor Extract Action should have a reasonable LIMIT:
- Set the LIMIT in the DataRaptor definition itself, not in the IP
- For user-facing queries, limit to 50-200 records
- For batch processing, limit to 2000 records per iteration
- For count/existence checks, limit to 1
Pagination Pattern
For large datasets that exceed a single extract's limit:
1. First extract: fetch records with LIMIT and OFFSET 0 2. Check if result count equals LIMIT (more records may exist) 3. Loop: increment OFFSET by LIMIT, re-extract, append results 4. Exit when result count is less than LIMIT
Avoid this pattern for datasets exceeding 10,000 records — use Apex Batch or a scheduled process instead.
---
10. Caching Strategies
When to Cache
- Read-only IPs that serve reference data (picklist values, configuration)
- IPs called frequently by multiple OmniScripts or FlexCards
- IPs whose source data changes infrequently
When NOT to Cache
- IPs that perform DML (creates, updates, deletes)
- IPs whose output depends on the current user's permissions or context
- IPs that return time-sensitive data (real-time pricing, inventory)
Cache Configuration
Set caching properties in the IP's PropertySet:
cacheType:Platformfor org-level cache,Sessionfor user-session cachecacheTTL: Time-to-live in seconds (e.g., 300 for 5 minutes, 3600 for 1 hour)
Cache Key Considerations
The cache key includes the IP's Type/SubType and the input JSON. Different inputs produce different cache entries. Normalize input data to maximize cache hit rates.
---
11. Batch vs Sequential Execution
Sequential (Default)
Elements execute one after another. Use when:
- Each step depends on the output of the previous step
- Strict ordering is required for business logic
- Error handling needs to stop execution on first failure
Batch / Parallel Opportunities
While IPs execute elements sequentially by default, you can optimize by:
- Grouping independent DataRaptor Extracts at the beginning of the procedure
- Using a single DataRaptor Extract with multiple output fields instead of multiple extracts
- Combining related DataRaptor Transforms into a single transform with multiple mappings
- Moving parallelizable logic into an Apex Remote Action that handles concurrent operations internally
Loop Block Performance
- Set appropriate batch sizes for Loop Blocks processing large collections
- Avoid nested loops — flatten data with a Transform first
- Minimize the number of elements inside a loop body
- Move invariant computations outside the loop
---
12. Reducing Redundant Calls
Common Redundancy Patterns
| Pattern | Problem | Fix |
|---|---|---|
| Same DataRaptor called twice | Duplicate SOQL queries | Extract once, reference the result in multiple downstream elements |
| Nested IP re-fetches parent data | Wasted queries | Pass data as input to the nested IP instead of re-fetching |
| Loop body fetches lookup data | N+1 query pattern | Fetch lookup data before the loop, reference it inside |
Data Passing vs Re-Fetching
Prefer passing data between elements over re-fetching:
- Use Set Values to copy data from one element's output to another element's input
- Pass context data to nested IP calls as input parameters
- Store intermediate results in variables for reuse
---
13. Versioning with Type/SubType
Versioning Strategy
- Use SubType to version IPs:
Type=AccountOnboarding,SubType=v1andSubType=v2 - Only one version of a Type/SubType pair can be active at a time
- Deploy and test the new version as inactive before deactivating the old version
- Update consumers (OmniScripts, FlexCards) to reference the new SubType after activation
Migration Checklist
1. Create the new IP with updated SubType 2. Deploy and test in sandbox with the new SubType 3. Update all consumers to reference the new SubType 4. Activate the new IP 5. Deactivate the old IP (do not delete until consumers are verified)
---
14. Documentation Standards
Procedure-Level Documentation
Every IP should have:
- A description field explaining the business purpose
- Input/output JSON contract documented in element descriptions or external documentation
- Dependency list (which DataRaptors, Apex classes, and nested IPs it requires)
Element-Level Documentation
Every element should have:
- A description explaining what it does and why
- Notes on expected input shape and output shape
- Error handling behavior documented
Maintenance Documentation
- Record the Type/SubType and the org(s) where the IP is deployed
- List all consumers (OmniScripts, FlexCards, Apex classes, other IPs) that call this IP
- Note any external API dependencies with endpoint URLs and authentication method
<!-- Parent: building-omnistudio-integration-procedure/SKILL.md -->
Integration Procedure Element Types Reference
Version: 1.0.0
Applies to: OmniStudio Integration Procedures (Core namespace and Vlocity managed package)
This reference documents every element type available in Integration Procedures, including PropertySetConfig JSON structures, input/output node mapping, and variable scoping rules.
---
Table of Contents
1. DataRaptor Extract Action 2. DataRaptor Load Action 3. DataRaptor Transform Action 4. Remote Action 5. Integration Procedure Action 6. HTTP Action 7. Conditional Block 8. Loop Block 9. Set Values 10. Variable Scoping and Data Passing 11. PropertySetConfig Common Properties
---
1. DataRaptor Extract Action
Reads data from Salesforce objects using a DataRaptor (Data Mapper) Extract definition.
PropertySetConfig
{
"bundle": "DRExtract_AccountDetails",
"disableFlushCacheForGet": false,
"useQueueableApexRemoting": false,
"additionalInput": {},
"additionalOutput": {},
"sendOnlyAdditionalInput": false,
"responseJSONPath": "",
"responseJSONNode": ""
}Key Properties
| Property | Type | Required | Description |
|---|---|---|---|
bundle | String | Yes | Name of the DataRaptor Extract definition |
additionalInput | Object | No | Extra key-value pairs merged into the DataRaptor input |
sendOnlyAdditionalInput | Boolean | No | If true, only sends additionalInput, ignoring upstream data |
responseJSONPath | String | No | JSONPath expression to extract a subset of the response |
responseJSONNode | String | No | Key name to wrap the response under |
disableFlushCacheForGet | Boolean | No | If true, uses cached DataRaptor results |
Input Mapping
The element receives the IP's current data context. To pass specific values to the DataRaptor:
- Use
additionalInputto map values from upstream elements:
{
"additionalInput": {
"AccountId": "%PreviousElement:accountId%"
}
}Output
The DataRaptor's output is stored under the element name in the IP response:
{
"GetAccountDetails": {
"Name": "Acme Corp",
"Industry": "Technology",
"BillingCity": "San Francisco"
}
}For multi-record results, the output is an array.
---
2. DataRaptor Load Action
Writes data to Salesforce objects using a DataRaptor (Data Mapper) Load definition.
PropertySetConfig
{
"bundle": "DRLoad_CreateContact",
"disableFlushCacheForGet": false,
"useQueueableApexRemoting": false,
"additionalInput": {},
"additionalOutput": {},
"sendOnlyAdditionalInput": false,
"responseJSONPath": "",
"responseJSONNode": ""
}Key Properties
| Property | Type | Required | Description |
|---|---|---|---|
bundle | String | Yes | Name of the DataRaptor Load definition |
additionalInput | Object | No | Extra key-value pairs merged into the DataRaptor input |
sendOnlyAdditionalInput | Boolean | No | If true, only sends additionalInput as input |
Input Mapping
Map upstream data into the fields the DataRaptor Load expects:
{
"additionalInput": {
"FirstName": "%SetInputValues:firstName%",
"LastName": "%SetInputValues:lastName%",
"AccountId": "%GetAccountDetails:Id%"
}
}Output
Returns the result of the DML operation:
{
"CreateContact": {
"Id": "003XXXXXXXXXXXX",
"errors": [],
"success": true
}
}Error Handling
Always follow a DataRaptor Load with error inspection. Check for:
successfield (boolean)errorsarray (contains error objects withstatusCodeandmessage)
---
3. DataRaptor Transform Action
Reshapes data without making any Salesforce queries or DML. Uses a DataRaptor Transform definition to map fields between JSON structures.
PropertySetConfig
{
"bundle": "DRTransform_FlattenAddress",
"additionalInput": {},
"additionalOutput": {},
"sendOnlyAdditionalInput": false,
"responseJSONPath": "",
"responseJSONNode": ""
}Key Properties
| Property | Type | Required | Description |
|---|---|---|---|
bundle | String | Yes | Name of the DataRaptor Transform definition |
additionalInput | Object | No | Extra key-value pairs to include in the transform input |
Use Cases
- Flatten nested JSON structures for downstream processing
- Rename fields to match external API contracts
- Combine data from multiple upstream elements into a single structure
- Extract specific fields from a large response
Input/Output
Input: The full IP data context or specific values via additionalInput Output: The transformed JSON structure, stored under the element name
---
4. Remote Action
Calls an Apex class method. The Apex class must implement the vlocity_cmt.VlocityOpenInterface (managed package) or omnistudio.VlocityOpenInterface2 (Core namespace) interface.
PropertySetConfig
{
"remoteClass": "AccountValidationService",
"remoteMethod": "validateAccount",
"additionalInput": {},
"additionalOutput": {},
"sendOnlyAdditionalInput": false,
"responseJSONPath": "",
"responseJSONNode": "",
"useQueueableApexRemoting": false,
"useFuture": false
}Key Properties
| Property | Type | Required | Description |
|---|---|---|---|
remoteClass | String | Yes | Apex class name |
remoteMethod | String | Yes | Method name to invoke |
useQueueableApexRemoting | Boolean | No | If true, executes as a Queueable job (async) |
useFuture | Boolean | No | If true, executes as a future method (async, no return value) |
Apex Interface
The Apex class must follow this pattern:
// Core namespace
global class AccountValidationService implements omnistudio.VlocityOpenInterface2 {
global Object invokeMethod(
String methodName,
Map<String, Object> inputMap,
Map<String, Object> outputMap,
Map<String, Object> options
) {
if (methodName == 'validateAccount') {
// Business logic here
String accountId = (String) inputMap.get('AccountId');
// ... validation logic ...
outputMap.put('isValid', true);
outputMap.put('validationMessages', new List<String>());
}
return null;
}
}Input Mapping
The IP data context is passed as inputMap. Use additionalInput to add or override values:
{
"additionalInput": {
"AccountId": "%GetAccountDetails:Id%",
"validationType": "full"
}
}Output
The outputMap contents are stored under the element name:
{
"ValidateAccount": {
"isValid": true,
"validationMessages": []
}
}---
5. Integration Procedure Action
Calls another Integration Procedure. Enables composition and reuse of IP logic.
PropertySetConfig
{
"ipMethod": "SharedLookup_Standard",
"additionalInput": {},
"additionalOutput": {},
"sendOnlyAdditionalInput": false,
"responseJSONPath": "",
"responseJSONNode": "",
"chainable": false
}Key Properties
| Property | Type | Required | Description |
|---|---|---|---|
ipMethod | String | Yes | The nested IP's Type_SubType identifier |
chainable | Boolean | No | If true, allows chaining outputs from the nested IP |
sendOnlyAdditionalInput | Boolean | No | If true, only sends additionalInput to the nested IP |
Input Mapping
By default, the entire IP data context is passed to the nested IP. Use sendOnlyAdditionalInput: true with additionalInput to send only specific data:
{
"ipMethod": "ContactLookup_Standard",
"sendOnlyAdditionalInput": true,
"additionalInput": {
"accountId": "%GetAccountDetails:Id%"
}
}Output
The nested IP's full response is stored under the calling element's name:
{
"LookupContacts": {
"contacts": [...],
"totalCount": 5
}
}Circular Dependency Prevention
Before configuring an IP Action, verify that the target IP does not call back to the current IP (directly or through intermediaries). Circular calls cause stack overflow errors at runtime.
---
6. HTTP Action
Makes an HTTP callout to an external API endpoint.
PropertySetConfig
{
"path": "https://api.example.com/v1/accounts",
"method": "POST",
"headers": {
"Content-Type": "application/json",
"Authorization": "Bearer %GetToken:accessToken%"
},
"body": {},
"additionalInput": {},
"additionalOutput": {},
"sendOnlyAdditionalInput": false,
"responseJSONPath": "",
"responseJSONNode": "",
"namedCredential": "",
"timeout": 30000
}Key Properties
| Property | Type | Required | Description |
|---|---|---|---|
path | String | Yes | Full URL or relative path (if using Named Credential) |
method | String | Yes | HTTP method: GET, POST, PUT, PATCH, DELETE |
headers | Object | No | Request headers as key-value pairs |
body | Object | No | Request body (for POST/PUT/PATCH) |
namedCredential | String | No | Salesforce Named Credential for authentication |
timeout | Number | No | Request timeout in milliseconds |
Security Best Practices
- Use Named Credentials for API authentication instead of hardcoded tokens
- Reference tokens from upstream elements (e.g., OAuth token retrieval) using
%elementName:key%syntax - Never store API keys in PropertySetConfig — use Custom Settings or Custom Metadata
Input Mapping
Use additionalInput to build dynamic request bodies:
{
"body": {
"accountName": "%GetAccountDetails:Name%",
"externalId": "%InputData:externalId%"
}
}Output
The HTTP response is stored under the element name:
{
"CallExternalAPI": {
"statusCode": 200,
"body": {
"result": "success",
"externalRecordId": "EXT-12345"
}
}
}Error Handling
Check statusCode in a downstream Conditional Block:
- 2xx: Success, proceed
- 4xx: Client error, log and return error response
- 5xx: Server error, consider retry or graceful degradation
---
7. Conditional Block
Evaluates conditions and branches execution based on the result. Contains child elements that execute only when the condition evaluates to true.
Configuration
Conditional Blocks do not use PropertySetConfig in the same way as action elements. Instead, conditions are defined in the element's condition configuration:
Condition Types
| Condition Type | Description | Example |
|---|---|---|
| Value Comparison | Compare two values | %GetAccount:Industry% EQUALS 'Technology' |
| Null Check | Check if a value is null/empty | %GetAccount:Id% IS NOT NULL |
| Boolean Check | Evaluate a boolean value | %ValidateInput:isValid% EQUALS true |
| Group (AND/OR) | Combine multiple conditions | condition1 AND condition2 |
Nested Elements
Elements inside a Conditional Block execute only when the condition is true. They follow the same ordering principles as top-level elements.
If/Else Pattern
Use two Conditional Blocks with complementary conditions:
1. Conditional Block A: condition = %check:value% EQUALS true -> contains success path elements 2. Conditional Block B: condition = %check:value% NOT EQUALS true -> contains error/alternate path elements
---
8. Loop Block
Iterates over a collection (array) and executes child elements for each item.
Configuration
| Property | Description |
|---|---|
| Loop collection | JSONPath to the array to iterate over (e.g., %GetContacts:records%) |
| Loop element variable | Variable name for the current item in each iteration |
Child Elements
Elements inside a Loop Block execute once per iteration. The current item is accessible via the loop element variable.
Performance Considerations
- Minimize the number of elements inside the loop body
- Avoid DataRaptor Extract/Load inside loops (N+1 pattern)
- Prefer bulk operations: collect data in the loop, perform a single DML after the loop
- Set appropriate batch sizes for large collections
- If loop body requires DML, consider using a single DataRaptor Load with a collection input instead
Example Pattern: Collect and Bulk Process
Instead of:
Loop -> DataRaptor Load (per item) // BAD: N DML operationsUse:
Loop -> Set Values (collect items into array)
DataRaptor Load (entire array) // GOOD: 1 DML operation---
9. Set Values
Assigns values to variables within the IP context. Used for initialization, response assembly, and data transformation.
Configuration
Set Values elements define key-value mappings:
| Mapping Type | Description | Example |
|---|---|---|
| Static value | Hardcoded constant | "status": "Processed" |
| Element reference | Value from upstream element | "accountName": "%GetAccount:Name%" |
| Formula | Computed value | Concatenation, conditional expressions |
| Input reference | Value from IP input | "requestId": "%input:requestId%" |
Common Uses
1. Initialize defaults: Set default values at the beginning of the procedure 2. Build output response: Assemble the final response JSON at the end 3. Intermediate variables: Store computed values for use in downstream conditions 4. Error response: Build standardized error response objects
Example: Response Assembly
Set Values: "BuildResponse"
out_success = true
out_accountId = %GetAccount:Id%
out_contactCount = %GetContacts:totalSize%
out_status = "Complete"---
10. Variable Scoping and Data Passing
Scope Rules
| Scope Level | Visibility | Lifetime |
|---|---|---|
| IP Input | All elements | Entire execution |
| Element Output | All downstream elements | Entire execution |
| Set Values Variable | All downstream elements | Entire execution |
| Loop Variable | Elements inside the loop body | Current iteration |
| Conditional Block Variable | Elements inside the block | Block execution |
| Nested IP Output | Calling element and downstream | Entire execution (under element name) |
Data Context
The IP maintains a cumulative data context. Each element's output is added to this context under the element's name. Downstream elements can reference any upstream element's output.
Reference Syntax
| Syntax | Description | Example |
|---|---|---|
%elementName:key% | Reference a specific key from an element's output | %GetAccount:Name% |
%elementName:nested.key% | Reference a nested key using dot notation | %GetAccount:BillingAddress.City% |
%elementName:array[0]% | Reference an array element by index | %GetContacts:records[0].Name% |
%input:key% | Reference an IP input parameter | %input:accountId% |
Data Passing to Nested IPs
When calling a nested IP via Integration Procedure Action:
- Default: The entire data context is passed as input
- Selective: Use
sendOnlyAdditionalInput: true+additionalInputto pass specific values - Output: The nested IP's response is namespaced under the calling element's name
Data Passing Best Practices
- Use
sendOnlyAdditionalInput: truefor nested IP calls to avoid leaking unnecessary data - Keep variable names consistent across elements for readability
- Document the expected data shape at each stage of the procedure
- Use Set Values to explicitly name and scope intermediate results rather than relying on implicit context merging
---
11. PropertySetConfig Common Properties
These properties are available on most action elements:
| Property | Type | Default | Description |
|---|---|---|---|
additionalInput | Object | {} | Extra key-value pairs added to the element's input |
additionalOutput | Object | {} | Extra key-value pairs added to the element's output |
sendOnlyAdditionalInput | Boolean | false | If true, only additionalInput is sent (upstream context excluded) |
responseJSONPath | String | "" | JSONPath to extract a subset of the element's response |
responseJSONNode | String | "" | Key name to wrap the response under |
useQueueableApexRemoting | Boolean | false | Execute asynchronously via Queueable Apex |
disableFlushCacheForGet | Boolean | false | Use cached results if available |
responseJSONPath vs responseJSONNode
responseJSONPath: Extracts a subset of the response. For example,$.recordsextracts only therecordsarray from the response.responseJSONNode: Wraps the response under a named key. For example, settingresponseJSONNodetoaccountDatawraps the entire response under{"accountData": {...}}.
These can be combined: first extract via path, then wrap under a node name.
additionalInput Merge Behavior
When sendOnlyAdditionalInput is false (default): 1. The IP's current data context is used as the base input 2. additionalInput key-value pairs are merged into this context 3. If a key exists in both, additionalInput takes precedence (override)
When sendOnlyAdditionalInput is true: 1. The data context is ignored 2. Only additionalInput key-value pairs are sent as input 3. Use this for nested IP calls where you want a clean input boundary
#!/bin/bash
# Integration Procedure CLI Commands
# Replace <Name> with the IP's Type_SubType name and <org> with your authenticated org alias.
# Query active Integration Procedures (add filters as needed)
sf data query -q "SELECT Id,Name,Type,SubType,IsActive FROM OmniProcess WHERE IsActive=true AND IsIntegrationProcedure=true LIMIT 200" -o <org>
# Query all Integration Procedures including inactive, sorted by most recently modified
sf data query -q "SELECT Id,Name,Type,SubType,IsActive,LastModifiedDate FROM OmniProcess WHERE IsIntegrationProcedure=true ORDER BY LastModifiedDate DESC LIMIT 200" -o <org>
# Retrieve an Integration Procedure from an org
sf project retrieve start -m OmniIntegrationProcedure:<Name> -o <org>
# Deploy an Integration Procedure to an org
sf project deploy start -m OmniIntegrationProcedure:<Name> -o <org>
# Deploy with dry-run validation first (recommended before production deploys)
sf project deploy start -m OmniIntegrationProcedure:<Name> -o <org> --dry-run
Related skills
Forks & variants (1)
Building Omnistudio Integration Procedure has 1 known copy in the catalog totaling 509 installs. They canonicalize to this original listing.
- forcedotcom - 509 installs
FAQ
What is building-omnistudio-integration-procedure?
OmniStudio Integration Procedure creation and validation with 110-point scoring. Use this skill when building server-side process orchestrations that combine Data Mapper actions, A
When should I use building-omnistudio-integration-procedure?
OmniStudio Integration Procedure creation and validation with 110-point scoring. Use this skill when building server-side process orchestrations that combine Data Mapper actions, A
Is building-omnistudio-integration-procedure safe to install?
Review the Security Audits panel on this page before production use.