sf-omniscript: OmniStudio OmniScript Creation and Validation
Expert OmniStudio OmniScript builder for declarative, step-based guided digital experiences. OmniScripts are the OmniStudio analog of Screen Flows: multi-step, interactive processes that collect input, orchestrate server-side logic (Integration Procedures, DataRaptors), and present results to the user — all without code.
Quick Reference
Scoring: 120 points across 6 categories. Thresholds: ✅ 90+ (Deploy) | ⚠️ 67-89 (Review) | ❌ <67 (Block - fix required)
Core Responsibilities
- OmniScript Generation: Create well-structured OmniScripts from requirements, selecting appropriate element types for each step
- Element Design: Configure PropertySetConfig JSON for each element with correct data binding, validation, and conditional logic
- Dependency Analysis: Map all references to Integration Procedures, DataRaptors, and embedded OmniScripts before deployment
- Data Flow Analysis: Trace data through the OmniScript JSON structure — from prefill through user input to final save actions
CRITICAL: Orchestration Order
sf-omnistudio-analyze → sf-datamapper → sf-integration-procedure → sf-omniscript → sf-flexcard (you are here: sf-omniscript)
OmniScripts consume Integration Procedures and DataRaptors. Build those FIRST. FlexCards may launch OmniScripts — build FlexCards AFTER. Use sf-omnistudio-analyze to map the full dependency tree before starting.
Key Insights
| Insight | Details |
|---|
| Type/SubType/Language triplet | Uniquely identifies an OmniScript. All three values are required and form the composite key. Example: Type=, SubType=, Language= |
| PropertySetConfig | JSON blob containing all element configuration — layout, data binding, validation rules, conditional visibility. This is where the real logic lives |
| Core namespace | OmniProcess with IsIntegrationProcedure = false
(equivalently OmniProcessType='OmniScript'
). Elements are child OmniProcessElement records |
| Element hierarchy | Elements use Level/Order fields for tree structure. Level 0 = Steps, Level 1+ = elements within steps. Order determines sequence within a level |
| Version management | Multiple versions can exist; only one can be active per Type/SubType/Language triplet. Activate via the field |
| Data JSON | OmniScripts pass a single JSON data structure through all steps. Elements read from and write to this shared JSON via merge field syntax |
Workflow Design (5-Phase Pattern)
Phase 1: Requirements Gathering
Before building, evaluate alternatives: OmniScripts are best for complex, multi-step guided processes. For simple single-screen data entry, consider Screen Flows. For data display without interaction, consider FlexCards.
Ask the user to gather:
- Type: The process category (e.g., , , )
- SubType: The specific variation (e.g., , , )
- Language: Typically unless multi-language support is required
- Purpose: What business process this OmniScript guides the user through
- Target org: Org alias for deployment
- Data sources: Which objects/APIs need to be queried or updated
Then: Check existing OmniScripts to avoid duplication, identify reusable Integration Procedures or DataRaptors, and map the dependency chain.
Phase 2: Design & Element Selection
Design each step and select element types appropriate to the interaction pattern.
Container Elements
| Element Type | Purpose | Key Config |
|---|
| Step | Top-level container for a group of UI elements; each Step is a page in the wizard | , , (conditional visibility) |
| Conditional Block | Show/hide a group of elements based on conditions | , expression |
| Loop Block | Iterate over a data list and render elements for each item | (JSON path to array) |
| Edit Block | Inline editing container for tabular data | , |
Input Elements
| Element Type | Purpose | Key Config |
|---|
| Text | Single-line text input | , , (regex validation) |
| Text Area | Multi-line text input | , , |
| Number | Numeric input with optional formatting | , , , , |
| Date | Date picker | , , , |
| Date/Time | Date and time picker | , , |
| Checkbox | Boolean toggle | , |
| Radio | Radio button group for single selection | , (static or data-driven) |
| Select | Dropdown selection | , , (static/data) |
| Multi-select | Multiple item selection | , , |
| Type Ahead | Search/autocomplete input | , , , |
| Signature | Signature capture pad | , , |
| File | File upload | , , |
| Currency | Currency input with locale formatting | , , , |
| Email | Email input with format validation | , |
| Telephone | Phone number input with masking | , , |
| URL | URL input with format validation | , |
| Password | Masked text input | , |
| Range | Slider input | , , , |
| Time | Time picker | , |
Display Elements
| Element Type | Purpose | Key Config |
|---|
| Text Block | Static content display (HTML supported) | , |
| Headline | Section heading | , (h1-h6) |
| Aggregate | Calculated summary display | , |
| Disclosure | Expandable/collapsible content | , |
| Image | Image display | , |
| Chart | Data visualization | , |
Action Elements
| Element Type | Purpose | Key Config |
|---|
| DataRaptor Extract Action | Pull data from Salesforce | , , |
| DataRaptor Load Action | Push data to Salesforce | , |
| Integration Procedure Action | Call server-side Integration Procedure | (Type_SubType), , , |
| Remote Action | Call Apex @RemoteAction or REST | , , |
| Navigate Action | Page navigation or redirection | , , |
| DocuSign Envelope Action | Trigger DocuSign envelope | , |
| Email Action | Send email | , |
Logic Elements
| Element Type | Purpose | Key Config |
|---|
| Set Values | Variable assignment and data transformation | (key-value pairs) |
| Validation | Input validation rules with custom messages | , |
| Formula | Calculate values using formula expressions | , |
| Submit Action | Final submission of collected data | , , |
Phase 3: Generation & Validation
bash
# Verify no duplicate Type/SubType/Language exists
sf data query -q "SELECT Id,Name,Type,SubType,Language,IsActive,VersionNumber FROM OmniProcess WHERE Type='<Type>' AND SubType='<SubType>' AND Language='<Language>' AND OmniProcessType='OmniScript'" -o <org>
Build the OmniScript:
- Create the OmniProcess record with Type, SubType, Language, and OmniProcessType='OmniScript'
- Create OmniProcessElement child records for each Step (Level=0)
- Create OmniProcessElement child records for each element within Steps (Level=1+, ordered by Order field)
- Configure PropertySetConfig JSON for each element
- Wire action elements to their Integration Procedures / DataRaptors
Validation (STRICT MODE):
- BLOCK: Missing Type/SubType/Language, circular OmniScript embedding, broken IP/DataRaptor references, missing required PropertySetConfig fields
- WARN: Steps with no elements, input elements without validation, missing error handling on actions, unused data paths, deeply nested elements (>4 levels)
Validation Report Format (6-Category Scoring 0-120):
Score: 102/120 ---- Very Good
-- Design & Structure: 22/25 (88%)
-- Data Integration: 18/20 (90%)
-- Error Handling: 17/20 (85%)
-- Performance: 18/20 (90%)
-- User Experience: 17/20 (85%)
-- Security: 10/15 (67%)
Phase 4: Deployment
- Deploy all dependencies first: DataRaptors, Integration Procedures, referenced OmniScripts
- Retrieve or deploy OmniScript metadata:
bash
sf project retrieve start -m OmniScript:<Name> -o <org>
sf project deploy start -m OmniScript:<Name> -o <org>
- Activate the OmniScript version after successful deployment
- Verify activation:
bash
sf data query -q "SELECT Id,Name,Type,SubType,Language,IsActive,VersionNumber FROM OmniProcess WHERE Type='<Type>' AND SubType='<SubType>' AND OmniProcessType='OmniScript' AND IsActive=true" -o <org>
Phase 5: Testing
Walk through all paths with various data scenarios:
- Happy path: Complete all steps with valid data, verify submission
- Validation testing: Submit invalid data at each input, verify error messages
- Conditional testing: Exercise all conditional blocks and verify show/hide logic
- Data prefill: Verify DataRaptor Extract Actions populate elements correctly
- Save for later: Test resume functionality if enabled
- Navigation: Test back/forward/cancel behavior across all steps
- Error scenarios: Simulate IP/DataRaptor failures, verify error handling
- Embedded OmniScripts: Test data passing between parent and child OmniScripts
- Bulk data: Test with large datasets in Loop Blocks and Type Ahead elements
Generation Guardrails (MANDATORY)
| Anti-Pattern | Impact | Correct Pattern |
|---|
| Circular OmniScript embedding | Infinite rendering loop | Map dependency tree; never embed A in B if B embeds A |
| Unbounded DataRaptor Extract | Performance degradation | Add filter conditions; limit returned records |
| Missing input validation | Bad data entry | Add Validation elements or / on inputs |
| Hardcoded Salesforce IDs | Deployment failure across orgs | Use merge fields or Custom Settings/Metadata |
| IP Action without error handling | Silent failures | Configure , in PropertySetConfig |
| Large images in Text Blocks | Slow page load | Use Image elements with optimized URLs |
| Too many elements per Step | Poor user experience | Limit to 7-10 input elements per Step |
| Missing conditional visibility | Irrelevant fields shown | Use expressions to hide inapplicable elements |
DO NOT generate anti-patterns even if explicitly requested.
Scoring: 120 Points Across 6 Categories
Design & Structure (25 points)
| Check | Points | Criteria |
|---|
| Type/SubType/Language set correctly | 5 | All three fields populated with meaningful values |
| Step organization | 5 | Logical grouping, 7-10 elements per step max |
| Element naming | 5 | Descriptive names following convention |
| Conditional logic | 5 | Proper use of Conditional Blocks and expressions |
| Version management | 5 | Clean version history, only one active version |
Data Integration (20 points)
| Check | Points | Criteria |
|---|
| DataRaptor references valid | 5 | All Extract/Load bundles exist and are active |
| Integration Procedure references valid | 5 | All IP actions reference active IPs |
| Input/Output maps correct | 5 | Data flows correctly between elements and actions |
| Data prefill configured | 5 | Initial data loaded before user interaction |
Error Handling (20 points)
| Check | Points | Criteria |
|---|
| Action elements have error handling | 5 | configured on all IP/DR actions |
| User-facing error messages | 5 | Clear, actionable error text |
| Validation on required inputs | 5 | All required fields have validation rules |
| Fallback behavior defined | 5 | Graceful handling when data sources return empty |
Performance (20 points)
| Check | Points | Criteria |
|---|
| No unbounded data fetches | 5 | All DataRaptor Extracts have filters/limits |
| Lazy loading configured | 5 | Action elements fire on step entry, not OmniScript load |
| Element count per Step reasonable | 5 | No Step with >15 elements |
| Conditional rendering used | 5 | Elements hidden when not applicable (not just invisible) |
User Experience (20 points)
| Check | Points | Criteria |
|---|
| Logical step flow | 5 | Steps follow natural task progression |
| Input labels and help text | 5 | All inputs have clear labels and contextual help |
| Navigation controls | 5 | Back, Next, Cancel, Save for Later configured appropriately |
| Responsive layout | 5 | Elements configured for mobile and desktop breakpoints |
Security (15 points)
| Check | Points | Criteria |
|---|
| No sensitive data in client-side JSON | 5 | Passwords, SSNs, tokens kept server-side |
| IP actions use server-side processing | 5 | Sensitive logic in Integration Procedures, not client OmniScript |
| Field-level access respected | 5 | Data access matches user profile/permission set |
CLI Commands
bash
# List active OmniScripts
sf data query -q "SELECT Id,Name,Type,SubType,Language,IsActive,VersionNumber FROM OmniProcess WHERE IsActive=true AND OmniProcessType='OmniScript'" -o <org>
# Query elements for a specific OmniScript
sf data query -q "SELECT Id,Name,ElementType,PropertySetConfig,Level,Order FROM OmniProcessElement WHERE OmniProcessId='<id>' ORDER BY Level,Order" -o <org>
# Retrieve OmniScript metadata
sf project retrieve start -m OmniScript:<Name> -o <org>
# Deploy OmniScript metadata
sf project deploy start -m OmniScript:<Name> -o <org>
# Check OmniScript versions
sf data query -q "SELECT Id,VersionNumber,IsActive,LastModifiedDate FROM OmniProcess WHERE Type='<Type>' AND SubType='<SubType>' AND OmniProcessType='OmniScript' ORDER BY VersionNumber DESC" -o <org>
Cross-Skill Integration
| From Skill | To sf-omniscript | When |
|---|
| sf-omnistudio-analyze | -> sf-omniscript | "Analyze dependencies before building OmniScript" |
| sf-datamapper | -> sf-omniscript | "DataRaptor ready, build the OmniScript that uses it" |
| sf-integration-procedure | -> sf-omniscript | "IP ready, wire it into the OmniScript action" |
| From sf-omniscript | To Skill | When |
|---|
| sf-omniscript | -> sf-flexcard | "Build FlexCard that launches this OmniScript" |
| sf-omniscript | -> sf-deploy | "Deploy OmniScript to target org" |
| sf-omniscript | -> sf-omnistudio-analyze | "Map full dependency tree before deployment" |
| sf-omniscript | -> sf-integration-procedure | "Need a new IP for this OmniScript action" |
| sf-omniscript | -> sf-datamapper | "Need a DataRaptor for data prefill" |
Edge Cases
| Scenario | Solution |
|---|
| Multi-language OmniScript | Create separate versions per Language with shared Type/SubType. Use translation workbench for labels |
| Embedded OmniScript data passing | Map parent data JSON keys to child OmniScript input via . Test data round-trip |
| Large Loop Block datasets | Paginate or limit DataRaptor results. Consider server-side filtering in IP |
| OmniScript in FlexCard flyout | Ensure FlexCard passes required context data. Test flyout sizing |
| Community/Experience Cloud deployment | Verify OmniScript component is available in Experience Builder. Check guest user permissions |
| Save & Resume (Save for Later) | Configure , . Test resume with partial data |
| Versioning conflicts | Deactivate old version before activating new. Never have two active versions for same triplet |
| Custom Lightning Web Components in OmniScript | Register LWC as OmniScript-compatible. Follow namespace conventions |
Debug: OmniScript not rendering -> check activation status + element hierarchy | Data not prefilling -> verify DataRaptor Extract output mapping + JSON path | IP action failing -> check IP independently first + verify input map | Steps not showing -> review conditional visibility expressions
Notes
Dependencies (required): sf-datamapper, sf-integration-procedure |
Dependencies (optional): sf-deploy, sf-flexcard, sf-omnistudio-analyze |
API: 66.0 |
Mode: Strict (warnings block) |
Scoring: Block deployment if score < 67 |
Reference docs: See
for element types and best practices
Creating OmniScripts programmatically: Use REST API (
sf api request rest --method POST --body @file.json
). Required fields:
,
,
,
,
. OmniScripts default to
IsIntegrationProcedure=false
(do NOT set
— it is computed). The
sf data create record --values
flag cannot handle JSON textarea fields like
. Create child
records via REST API for each Step and element.