Salesforce · · 44 min read

OmniStudio in Salesforce

A complete guide to Salesforce OmniStudio — FlexCards, OmniScripts, DataRaptors (Data Mappers), Integration Procedures, and the Business Rules Engine.

Part 27: OmniStudio in Salesforce

If you have ever wanted to build guided, multi-step user experiences in Salesforce without writing a single line of Apex or LWC code, OmniStudio is the tool for you. It is a declarative framework that lets admins and developers create rich, interactive interfaces, pull data from multiple sources, transform it, and push it back — all through a visual drag-and-drop experience. In this part of our Salesforce series, we are going to break down every major component of OmniStudio: FlexCards, OmniScripts, Data Mappers (formerly DataRaptors), Integration Procedures, and the Business Rules Engine. We will also walk through two hands-on projects so you can see how these pieces come together in practice.


What is OmniStudio?

OmniStudio is a suite of declarative tools within the Salesforce platform designed to help you build industry-specific, guided digital experiences. It was originally developed by Vlocity (acquired by Salesforce in 2020) and has since been integrated natively into Salesforce as part of the Industries Cloud offerings. However, you do not need an Industries Cloud license to use it — OmniStudio is available as a standalone package as well.

At its core, OmniStudio provides five main components that work together:

ComponentWhat It Does
FlexCardsDisplay data from Salesforce or external sources in a compact, card-based UI
OmniScriptsBuild guided, multi-step forms and wizards for data entry and process automation
Data Mappers (DataRaptors)Read, transform, and write data between Salesforce objects and JSON/XML structures
Integration ProceduresOrchestrate multiple data operations (reads, writes, transformations, HTTP callouts) server-side
Business Rules Engine (BRE)Define decision matrices and expression sets for complex business logic

Why OmniStudio Matters

Traditional Salesforce development often requires a combination of Apex, Lightning Web Components, and Flows to create complex user experiences. OmniStudio replaces much of that custom work with a visual toolset that is:

  • Faster to build — drag-and-drop components instead of writing code
  • Easier to maintain — business users and admins can modify configurations without developer involvement
  • Industry-aligned — built-in templates and patterns for industries like healthcare, financial services, communications, and energy
  • Omni-channel ready — the same OmniScript or FlexCard can render on a Lightning page, an Experience Cloud site, or a mobile app

OmniStudio vs. Flows

A question that comes up frequently is: when should I use OmniStudio instead of Flows? Here is a comparison:

CriteriaOmniStudioFlows
Multi-step guided formsPurpose-built for thisPossible but clunkier with Screen Flows
Complex data transformationsData Mappers and Integration Procedures handle this nativelyRequires Apex invocable actions or multiple flow elements
External integrationsIntegration Procedures with HTTP actions built inRequires Apex or External Services
Card-based data displayFlexCards are designed for thisNot natively supported
Decision logicBusiness Rules Engine with matrices and expressionsDecision elements and formulas
Learning curveSteeper — separate toolset to learnLower — most admins already know Flows
LicensingMay require additional licenses depending on your editionIncluded with most Salesforce editions
Best forComplex, multi-step processes with integrationsSimple to moderate automation and screen flows

The short answer: if you need a rich, multi-step guided experience that pulls from or pushes to multiple data sources (including external systems), OmniStudio is probably the better fit. For simpler automation tasks, Flows remain the go-to.

How to Enable OmniStudio

Before you can use OmniStudio, you need to make sure it is available in your org:

  1. Check your license — OmniStudio requires either an Industries Cloud license or the standalone OmniStudio license. In Developer Edition orgs, you can enable it from Setup.
  2. Go to Setup > OmniStudio Settings
  3. Toggle on Enable OmniStudio if it is not already enabled
  4. Toggle on Enable OmniStudio Metadata — this is the newer standard runtime that uses metadata API-based deployment (as opposed to the legacy managed package approach)
  5. Assign the OmniStudio Admin or OmniStudio User permission set to your user

Important: Salesforce has been migrating OmniStudio from a managed package model to a standard (metadata-based) model. In new implementations, always use the standard OmniStudio runtime. The legacy Vlocity managed package is being phased out.

The OmniStudio Home Tab

Once OmniStudio is enabled, you will find a new OmniStudio app in the App Launcher. The home tab gives you a dashboard-style view of all your OmniStudio components:

  • FlexCards
  • OmniScripts
  • Data Mappers (DataRaptors)
  • Integration Procedures
  • Expression Sets
  • Decision Matrices

Each component type has its own dedicated designer — a visual canvas where you drag, drop, and configure elements.


How to Create a FlexCard

FlexCards are lightweight, reusable UI components that display contextual data to users. Think of them as smart data cards that pull information from one or more data sources and present it in a compact layout. You can embed FlexCards inside Lightning pages, OmniScripts, Experience Cloud sites, or even inside other FlexCards.

FlexCard Anatomy

A FlexCard consists of:

  • Data Sources — where the card gets its data (Salesforce objects, Integration Procedures, Apex classes, REST APIs, or static data)
  • States — different visual layouts that the card can display based on conditions (e.g., show a different layout when a case is escalated vs. normal)
  • Elements — the visual building blocks within a state (text, fields, icons, images, actions, charts, child FlexCards)
  • Actions — things users can do from the card (launch an OmniScript, navigate to a record, call a flow, open a URL)

Step-by-Step: Creating a FlexCard

Let us build a FlexCard that shows Account information.

Step 1: Navigate to FlexCards

  1. Open the OmniStudio app from the App Launcher
  2. Click on the FlexCards tab
  3. Click New to create a new FlexCard

Step 2: Set FlexCard Properties

  1. Give your FlexCard a Name — for example, AccountSummaryCard
  2. Optionally set an Author name
  3. Set the Data Source Type — choose SOQL for a straightforward Salesforce query
  4. In the Data Source configuration, enter your query:
SELECT Id, Name, Industry, AnnualRevenue, Phone, BillingCity, BillingState
FROM Account
WHERE Id = ':recordId'

Note: The :recordId parameter is a merge field that automatically resolves to the current record’s Id when the FlexCard is placed on a Lightning record page.

  1. Click Save & Fetch to test the data source and pull sample data into the designer

Step 3: Design the Layout

Once data is fetched, you will see the FlexCard designer canvas. Now you can drag and drop elements:

  1. Add a Text element — set its value to {Name} to display the account name. Style it as a heading.
  2. Add a Field element — configure it to show Industry with a label
  3. Add another Field element — configure it for AnnualRevenue and set the format to currency
  4. Add an Icon element — choose the standard:account icon to give the card a visual identity
  5. Add a Block element — use this to group the phone and billing address fields horizontally

Your layout might look something like this conceptually:

+-----------------------------------------------+
| [Account Icon]  Acme Corporation               |
|                                                 |
| Industry: Technology    Revenue: $5,000,000     |
| Phone: (555) 123-4567                           |
| Location: San Francisco, CA                     |
|                                                 |
| [View Record]  [Create Case]  [Send Email]      |
+-----------------------------------------------+

Step 4: Add Actions

Actions are what make FlexCards interactive. Let us add a few:

  1. Click on the Actions section of the designer
  2. Add a Navigate action — set the target to {/lightning/r/Account/{Id}/view} to let users jump to the full account record
  3. Add an OmniScript action — link it to an OmniScript (we will build one later) that lets users create a case for this account
  4. Add a URL action — point it to mailto:{Email} to open the user’s email client

Step 5: Configure States

States allow you to show different layouts based on conditions. For example:

  1. Default State — the layout we just built
  2. High Value State — a different layout (maybe with a gold banner) that shows when AnnualRevenue > 1000000

To add a state:

  1. Click New State in the designer
  2. Name it HighValue
  3. Set the Condition to: {AnnualRevenue} > 1000000
  4. Design a different layout for this state — perhaps with a highlighted background and additional fields

The FlexCard engine evaluates conditions top-to-bottom and renders the first matching state.

Step 6: Preview and Activate

  1. Use the Preview panel on the right side of the designer to see how your card looks with real data
  2. Toggle between different records to test all states
  3. When satisfied, click Activate to make the FlexCard available for use

Embedding a FlexCard

Once activated, you can embed your FlexCard in several places:

PlacementHow
Lightning Record PageAdd the OmniStudio FlexCard component in Lightning App Builder
Experience Cloud SiteDrag the FlexCard component into Experience Builder
OmniScriptUse the FlexCard element inside an OmniScript step
Another FlexCardUse the Child FlexCard element to nest cards

When placing a FlexCard on a Lightning record page, the recordId context variable is automatically passed to the card, so your data source query resolves correctly.

FlexCard Best Practices

  • Keep it lightweight — FlexCards should show summary data, not entire record detail pages. If users need to see everything, link them to the full record.
  • Use states wisely — do not create dozens of states. Keep it to 3-4 maximum for maintainability.
  • Cache your data sources — if the data does not change frequently, enable caching on the data source to improve performance.
  • Use Integration Procedures as data sources when you need to combine data from multiple objects or external systems. They are more performant than making multiple individual queries.
  • Name your FlexCards descriptively — use a naming convention like ObjectName_Purpose (e.g., Account_SummaryCard, Case_EscalationCard).

How to Create an OmniScript

OmniScripts are the guided, multi-step experiences that walk users through a process. Think of them as sophisticated wizards — they can collect user input across multiple steps, validate data, call integrations, perform calculations, and submit the results back to Salesforce or an external system.

OmniScript Anatomy

An OmniScript consists of:

  • Type and Sub Type — categorize the script (e.g., Type: Case, Sub Type: CreateCase)
  • Language — the language of the script (supports multiple languages for the same Type/Sub Type)
  • Steps — the pages or screens that the user navigates through
  • Elements — the components within each step (text inputs, dropdowns, radio buttons, type-ahead lookups, data tables, FlexCards, etc.)
  • Actions — server-side operations that run during the script (Integration Procedure calls, Data Mapper calls, HTTP actions, Apex Remote Actions, etc.)
  • Conditions and Formulas — show/hide logic and computed values

Step-by-Step: Creating an OmniScript

Let us build an OmniScript that allows users to create a new Case for an Account.

Step 1: Navigate to OmniScripts

  1. Open the OmniStudio app
  2. Click on the OmniScripts tab
  3. Click New

Step 2: Set Script Properties

  1. Type: Case
  2. Sub Type: CreateCase
  3. Language: English
  4. Version: 1 (auto-assigned)
  5. Click Save

This opens the OmniScript designer.

Step 3: Add Steps

Steps are the pages of your wizard. Drag a Step element from the toolbox onto the canvas.

Step 1: Account Information

  1. Drag a Step element onto the canvas
  2. Name it StepAccountInfo
  3. Set the Label to Account Information
  4. Inside this step, add the following elements:
Element TypeNameConfiguration
Type Ahead BlockAccountLookupData Source: SOQL query on Account; Display Field: Name; Value Field: Id
Text BlockContactNameLabel: Contact Name; Required: true
EmailContactEmailLabel: Contact Email; Required: true
TelephoneContactPhoneLabel: Contact Phone

Step 2: Case Details

  1. Drag another Step element onto the canvas
  2. Name it StepCaseDetails
  3. Set the Label to Case Details
  4. Add these elements:
Element TypeNameConfiguration
Select (Dropdown)CaseOriginOptions: Phone, Email, Web, Chat; Required: true
Select (Dropdown)CasePriorityOptions: Low, Medium, High, Critical; Default: Medium
Select (Dropdown)CaseTypeOptions: Problem, Feature Request, Question
Text AreaCaseSubjectLabel: Subject; Required: true; Max Length: 255
Text Area (Rich)CaseDescriptionLabel: Description; Max Length: 32000

Step 3: Review and Submit

  1. Drag a final Step element onto the canvas
  2. Name it StepReview
  3. Set the Label to Review & Submit
  4. Add a Formula element to build a summary display
  5. Add a Submit action (we will configure this next)

Step 4: Add Conditional Logic

OmniScripts support show/hide conditions on any element. For example, let us show an additional “Escalation Reason” field only when the priority is Critical:

  1. Add a Text Area element named EscalationReason inside the Case Details step
  2. In the element’s properties, find the Show If condition
  3. Set it to:
%CaseDetails:CasePriority% == "Critical"

This uses OmniScript merge syntax — %StepName:ElementName% references a value from another element.

Step 5: Configure Data Actions

Now we need to wire up the submit action so the OmniScript actually creates a Case record in Salesforce.

Option A: Using a Data Mapper (DataRaptor)

  1. Create a DataRaptor Turbo Extract to read the Account data (we will cover Data Mappers in detail in the next section)
  2. Create a DataRaptor Load to write the Case record
  3. In the OmniScript, add a DataRaptor Post Action element to the Review step
  4. Map the OmniScript fields to the DataRaptor input fields

Option B: Using an Integration Procedure

  1. Create an Integration Procedure that handles the Case creation logic (also covered below)
  2. In the OmniScript, add an Integration Procedure Action element
  3. Map the OmniScript fields to the Integration Procedure input

Option C: Using a Direct SOQL/DML Action

For simple scenarios, OmniScripts support direct DML actions:

  1. Add a DataRaptor Post Action or use the built-in Save Action
  2. Configure the mapping between OmniScript elements and the Case object fields:
{
  "AccountId": "%StepAccountInfo:AccountLookup:Id%",
  "ContactName": "%StepAccountInfo:ContactName%",
  "ContactEmail": "%StepAccountInfo:ContactEmail%",
  "Origin": "%StepCaseDetails:CaseOrigin%",
  "Priority": "%StepCaseDetails:CasePriority%",
  "Type": "%StepCaseDetails:CaseType%",
  "Subject": "%StepCaseDetails:CaseSubject%",
  "Description": "%StepCaseDetails:CaseDescription%"
}

Step 6: Add Navigation and Validation

OmniScripts have built-in navigation (Next, Previous, Save for Later) but you can customize it:

  1. Validation Groups — group required fields together and prevent the user from moving to the next step until all are filled
  2. Custom Navigation — override the default Next/Previous buttons with conditional navigation (e.g., skip Step 2 if a certain condition is met)
  3. Save for Later — enable this in the script properties to let users save their progress and resume later

To set up validation:

  1. Select the Step element
  2. In properties, enable Validation
  3. Each required element within the step will be validated before the user can proceed

Step 7: Preview, Test, and Activate

  1. Click Preview in the designer toolbar
  2. Walk through the script as a user would — fill in fields, navigate between steps, and submit
  3. Check the debug log panel at the bottom to see the JSON data structure being built
  4. Once everything works correctly, click Activate

OmniScript JSON Structure

One of the most powerful aspects of OmniScripts is that they build a JSON data structure as the user fills in fields. This JSON is what gets passed to your Integration Procedures, Data Mappers, or submit actions. Understanding this structure is key to mapping data correctly.

As the user fills out our Case creation script, the JSON looks like this:

{
  "StepAccountInfo": {
    "AccountLookup": {
      "Id": "001xx000003ABCDEFG",
      "Name": "Acme Corporation"
    },
    "ContactName": "John Smith",
    "ContactEmail": "john.smith@acme.com",
    "ContactPhone": "(555) 123-4567"
  },
  "StepCaseDetails": {
    "CaseOrigin": "Email",
    "CasePriority": "High",
    "CaseType": "Problem",
    "CaseSubject": "Unable to login to portal",
    "CaseDescription": "Customer reports intermittent login failures since last Tuesday."
  }
}

This nested JSON structure follows the hierarchy of Steps and Elements. When mapping to a Data Mapper or Integration Procedure, you reference fields using this path notation.

Embedding an OmniScript

You can launch OmniScripts from several places:

Launch MethodHow
Lightning Record PageAdd the OmniScript component in Lightning App Builder
FlexCard ActionAdd an action button on a FlexCard that opens the OmniScript
Experience Cloud SiteEmbed the OmniScript component in Experience Builder
URLNavigate directly using the OmniScript URL pattern
FlowLaunch from a Flow using an OmniScript action
Another OmniScriptNest one script inside another

OmniScript Best Practices

  • Keep steps focused — each step should have a clear purpose. Do not cram 20 fields into one step.
  • Use Type Ahead blocks instead of free-text fields when the user needs to select from existing records. This prevents data quality issues.
  • Leverage conditional visibility — hide fields and steps that are not relevant to reduce cognitive load.
  • Test with different data scenarios — use the Preview panel to test edge cases (missing data, null values, large datasets).
  • Version your OmniScripts — when making changes, create a new version instead of modifying the active one. This lets you roll back if something breaks.
  • Use Integration Procedures for submit actions — they give you more control and error handling than direct DML actions.

How to Set Up a Data Mapper (DataRaptor)

Data Mappers — originally called DataRaptors in the Vlocity days, and still referred to as DataRaptors in many orgs — are the data transformation engine of OmniStudio. They handle reading data from Salesforce, transforming it, and writing it back. Think of them as the bridge between your UI components (FlexCards, OmniScripts) and your Salesforce data.

Types of Data Mappers

There are three types of Data Mappers:

TypeWhat It DoesWhen to Use It
Turbo ExtractReads data from a single Salesforce object (with related objects via lookups)When you need to fetch data for a FlexCard or OmniScript
ExtractReads data from multiple Salesforce objects with complex joinsWhen Turbo Extract is not flexible enough
LoadWrites (creates or updates) data to one or more Salesforce objectsWhen an OmniScript or Integration Procedure needs to save data
TransformConverts data from one JSON/XML structure to another without touching Salesforce objectsWhen you need to reshape data between systems

Note: Turbo Extract is newer and more performant than the standard Extract. Use Turbo Extract whenever possible. The standard Extract exists for backward compatibility and complex multi-object scenarios.

Step-by-Step: Creating a Turbo Extract Data Mapper

Let us create a Data Mapper that reads Account data along with related Contacts and open Cases.

Step 1: Navigate to Data Mappers

  1. Open the OmniStudio app
  2. Click on the DataRaptors tab (or Data Mappers depending on your org version)
  3. Click New
  4. Select Turbo Extract

Step 2: Configure the Extract

  1. Name: Account_WithContactsAndCases
  2. Interface Name: auto-generated from the name
  3. Primary Object: Account

Step 3: Define the Output Fields

In the Fields tab, add the fields you want to extract. Click Add Field for each:

ObjectFieldOutput Path
AccountIdAccountId
AccountNameAccountName
AccountIndustryIndustry
AccountAnnualRevenueRevenue
AccountPhonePhone
Contact (child)IdContacts:Id
Contact (child)NameContacts:Name
Contact (child)EmailContacts:Email
Case (child)IdOpenCases:Id
Case (child)SubjectOpenCases:Subject
Case (child)StatusOpenCases:Status
Case (child)PriorityOpenCases:Priority

The colon notation (Contacts:Name) tells the Data Mapper to nest the data under a Contacts array in the output JSON.

Step 4: Add Filters

  1. In the Filter section, add a filter for the Account:

    • Field: Id
    • Operator: Equals
    • Value: :recordId (input parameter)
  2. For the Cases, add a filter:

    • Field: IsClosed
    • Operator: Equals
    • Value: false

Step 5: Test the Data Mapper

  1. Click the Preview tab
  2. Enter a test Account Id in the input parameters
  3. Click Execute
  4. Review the output JSON:
{
  "AccountId": "001xx000003ABCDEFG",
  "AccountName": "Acme Corporation",
  "Industry": "Technology",
  "Revenue": 5000000,
  "Phone": "(555) 123-4567",
  "Contacts": [
    {
      "Id": "003xx000004HIJKLM",
      "Name": "John Smith",
      "Email": "john@acme.com"
    },
    {
      "Id": "003xx000004NOPQRS",
      "Name": "Jane Doe",
      "Email": "jane@acme.com"
    }
  ],
  "OpenCases": [
    {
      "Id": "500xx000005TUVWXY",
      "Subject": "Login issue",
      "Status": "New",
      "Priority": "High"
    }
  ]
}
  1. Click Activate once the output looks correct

Step-by-Step: Creating a Load Data Mapper

Now let us create a Data Mapper that writes Case records to Salesforce.

Step 1: Create a New Data Mapper

  1. Click New on the DataRaptors tab
  2. Select Load
  3. Name: Case_CreateFromOmniScript

Step 2: Define the Input-to-Object Mapping

In the Fields tab, map the incoming JSON fields to Salesforce object fields:

Input PathSalesforce ObjectSalesforce FieldMapping Notes
AccountIdCaseAccountIdLookup to parent Account
ContactNameCaseSuppliedNameMaps to web-supplied name
ContactEmailCaseSuppliedEmailMaps to web-supplied email
OriginCaseOriginPicklist value
PriorityCasePriorityPicklist value
TypeCaseTypePicklist value
SubjectCaseSubjectFree text
DescriptionCaseDescriptionFree text

Step 3: Configure the DML Operation

  1. Set the Operation to Insert (since we are creating new Cases)
  2. Optionally set Upsert with an external ID field if you need to handle duplicates

Step 4: Test and Activate

  1. In the Preview tab, paste sample input JSON:
{
  "AccountId": "001xx000003ABCDEFG",
  "ContactName": "John Smith",
  "ContactEmail": "john@acme.com",
  "Origin": "Web",
  "Priority": "High",
  "Type": "Problem",
  "Subject": "Portal login issue",
  "Description": "Customer cannot log in since Tuesday"
}
  1. Click Execute (this will actually create a record in your org — use a sandbox)
  2. Verify the Case was created correctly in Salesforce
  3. Activate the Data Mapper

Step-by-Step: Creating a Transform Data Mapper

Transform Data Mappers are used when you need to reshape data between two different JSON structures without reading from or writing to Salesforce.

Common use cases:

  • Converting an external API response into a format your OmniScript expects
  • Reshaping OmniScript output JSON before passing it to an external system
  • Flattening nested data structures or creating nested structures from flat data

Example: Flattening Nested Contact Data

Input JSON:

{
  "account": {
    "name": "Acme Corp",
    "contacts": [
      {
        "first": "John",
        "last": "Smith",
        "email": "john@acme.com"
      }
    ]
  }
}

Desired Output JSON:

{
  "AccountName": "Acme Corp",
  "PrimaryContactFirstName": "John",
  "PrimaryContactLastName": "Smith",
  "PrimaryContactEmail": "john@acme.com"
}

Mapping configuration:

Input PathOutput Path
account:nameAccountName
account:contacts[0]:firstPrimaryContactFirstName
account:contacts[0]:lastPrimaryContactLastName
account:contacts[0]:emailPrimaryContactEmail

The array index [0] grabs the first element from the contacts array.

Data Mapper Best Practices

  • Use Turbo Extract over standard Extract whenever possible — it is significantly faster.
  • Keep field lists lean — only extract the fields you actually need. Fetching entire objects slows things down.
  • Use meaningful output paths — name your output JSON fields clearly so downstream consumers (FlexCards, OmniScripts, Integration Procedures) can reference them easily.
  • Test with edge cases — what happens if there are no child records? What if a field is null? Make sure your downstream components handle empty arrays and null values gracefully.
  • Version your Data Mappers — just like OmniScripts, create new versions when making changes.

How to Set Up an Integration Procedure

Integration Procedures are the server-side orchestration engine of OmniStudio. They let you chain together multiple data operations — Data Mapper calls, HTTP callouts, matrix lookups, response transformations — into a single, efficient server-side process. Think of them as the backend logic layer that powers your FlexCards and OmniScripts.

Why Use Integration Procedures?

You might wonder: why not just call Data Mappers directly from an OmniScript or FlexCard? Here are the key reasons:

  1. Performance — Integration Procedures run entirely server-side in a single transaction. Calling multiple Data Mappers directly from a FlexCard would require multiple round trips between the client and server.
  2. Orchestration — you can chain operations sequentially or run them in parallel, with conditional branching.
  3. External integrations — Integration Procedures can make HTTP callouts to external APIs, which FlexCards and Data Mappers cannot do on their own.
  4. Error handling — you can catch errors from one step and handle them gracefully before returning data to the UI.
  5. Reusability — one Integration Procedure can serve multiple FlexCards and OmniScripts.

Integration Procedure Elements

ElementWhat It Does
DataRaptor Extract ActionCalls a Turbo Extract or Extract Data Mapper
DataRaptor Post ActionCalls a Load Data Mapper to write data
DataRaptor Transform ActionCalls a Transform Data Mapper
HTTP ActionMakes an HTTP callout to an external REST API
Matrix ActionLooks up a value in a Decision Matrix (part of the Business Rules Engine)
Expression Set ActionEvaluates an Expression Set (also part of the BRE)
Set ValuesManually set or transform values in the data JSON
Conditional BlockBranch logic based on conditions
Loop BlockIterate over an array of items
Response ActionDefine what gets returned to the caller

Step-by-Step: Creating an Integration Procedure

Let us build an Integration Procedure that fetches an Account’s details, enriches it with data from an external credit-check API, and returns the combined result to a FlexCard.

Step 1: Create the Integration Procedure

  1. Open the OmniStudio app
  2. Click on Integration Procedures
  3. Click New
  4. Set the properties:
    • Type: Account
    • Sub Type: GetEnrichedData
    • Language: English
  5. Click Save

Step 2: Add a Data Mapper Extract

  1. Drag a DataRaptor Extract Action onto the canvas
  2. Name it ExtractAccountData
  3. Set the DataRaptor Interface to the Account_WithContactsAndCases Turbo Extract we built earlier
  4. Configure the input mapping:
{
  "recordId": "%input.AccountId%"
}

The %input.AccountId% syntax references the input parameter passed to the Integration Procedure.

  1. Set the Output key to AccountData — this is where the extract results will be stored in the procedure’s JSON context

Step 3: Add an HTTP Callout

  1. Drag an HTTP Action onto the canvas (below the extract action)
  2. Name it CreditCheckAPI
  3. Configure the HTTP settings:
SettingValue
Named CredentialCreditCheckService (set up in Setup > Named Credentials)
Path/api/v2/credit-check
MethodPOST
Request BodySee below

Request body configuration:

{
  "companyName": "%AccountData.AccountName%",
  "phone": "%AccountData.Phone%",
  "industry": "%AccountData.Industry%"
}

Notice how we reference the output from the previous step (AccountData.AccountName) using the merge syntax.

  1. Set the Output key to CreditData

Step 4: Add a Conditional Block

What if the credit check API returns an error? Let us handle that:

  1. Drag a Conditional Block onto the canvas after the HTTP action
  2. Name it CheckCreditResponse
  3. Set the condition: %CreditData.statusCode% == 200

Inside the True branch:

  • Add a Set Values element to map the credit score:
{
  "CreditScore": "%CreditData.response.score%",
  "CreditRating": "%CreditData.response.rating%",
  "CreditStatus": "Available"
}

Inside the False branch:

  • Add a Set Values element to set default values:
{
  "CreditScore": null,
  "CreditRating": "Unknown",
  "CreditStatus": "Unavailable - API Error"
}

Step 5: Add a Response Action

  1. Drag a Response Action onto the canvas at the very end
  2. Name it FinalResponse
  3. Configure the response to include all the data the FlexCard needs:
{
  "AccountId": "%AccountData.AccountId%",
  "AccountName": "%AccountData.AccountName%",
  "Industry": "%AccountData.Industry%",
  "Revenue": "%AccountData.Revenue%",
  "Phone": "%AccountData.Phone%",
  "Contacts": "%AccountData.Contacts%",
  "OpenCases": "%AccountData.OpenCases%",
  "CreditScore": "%CreditScore%",
  "CreditRating": "%CreditRating%",
  "CreditStatus": "%CreditStatus%"
}

Step 6: Test and Activate

  1. Click Preview
  2. Enter test input:
{
  "AccountId": "001xx000003ABCDEFG"
}
  1. Click Execute and review the output
  2. Check each step’s individual output in the execution log to debug any issues
  3. Once everything works, click Activate

Using the Integration Procedure

Now you can reference this Integration Procedure from:

  • FlexCards — set the data source type to “Integration Procedure” and enter Account.GetEnrichedData as the procedure name
  • OmniScripts — add an Integration Procedure Action element
  • Apex — call it programmatically using the omnistudio.IntegrationProcedureService class
  • REST API — invoke it via the OmniStudio REST endpoint

Integration Procedure Best Practices

  • Use Named Credentials for all HTTP callouts — never hardcode URLs or credentials.
  • Run steps in parallel when they are independent of each other. The Integration Procedure designer lets you place elements side by side to indicate parallel execution.
  • Add error handling — always plan for what happens when an external API is down or a Data Mapper returns no results.
  • Keep procedures focused — one procedure should handle one logical operation. If you need to do many things, create multiple procedures and chain them.
  • Monitor performance — use the execution log to identify slow steps and optimize them.
  • Cache results — if the Integration Procedure data does not change frequently, enable caching at the FlexCard level.

How to Use the Business Rules Engine

The Business Rules Engine (BRE) is the decision-making toolkit within OmniStudio. It allows you to define complex business logic through a visual interface rather than writing code. The BRE has two main components: Decision Matrices and Expression Sets.

Decision Matrices

A Decision Matrix is essentially a lookup table. You define rows of conditions and corresponding output values. When called, the matrix evaluates the input against the rows and returns the matching output.

When to Use Decision Matrices

  • Pricing rules — look up a price based on product, quantity, and region
  • Eligibility checks — determine if a customer qualifies for a program based on age, income, and location
  • Routing logic — determine which queue or team should handle a case based on priority, type, and product
  • Tax calculations — look up tax rates by state, product category, and customer type

Step-by-Step: Creating a Decision Matrix

Let us create a matrix that determines the support tier for a customer based on their annual revenue and industry.

Step 1: Navigate to Decision Matrices

  1. Open the OmniStudio app
  2. Click on the Decision Matrices tab (sometimes under Business Rules)
  3. Click New

Step 2: Define the Matrix

  1. Name: SupportTierMatrix
  2. Type: Standard (or Grouped for more complex scenarios)

Step 3: Define Input and Output Columns

Column NameColumn TypeData Type
AnnualRevenueInput (Range)Currency
IndustryInput (Exact Match)Text
SupportTierOutputText
ResponseTimeSLAOutputNumber

Step 4: Populate the Matrix Rows

AnnualRevenue MinAnnualRevenue MaxIndustrySupportTierResponseTimeSLA (hours)
0100000*Bronze48
100001500000*Silver24
5000011000000*Gold8
1000001999999999*Platinum4
500001999999999HealthcarePlatinum2
500001999999999Financial ServicesPlatinum2

The * wildcard in the Industry column means “any industry.” More specific rows (like Healthcare and Financial Services) take precedence over wildcard rows when there is a match.

Step 5: Test the Matrix

  1. Click Preview
  2. Enter test inputs:
{
  "AnnualRevenue": 750000,
  "Industry": "Healthcare"
}
  1. Expected output:
{
  "SupportTier": "Platinum",
  "ResponseTimeSLA": 2
}

The Healthcare row matches because it is more specific than the wildcard row for the same revenue range.

  1. Activate the matrix

Expression Sets

Expression Sets are the other half of the Business Rules Engine. While Decision Matrices are lookup tables, Expression Sets are calculation engines. They let you define variables, constants, mathematical formulas, conditional logic, and function calls — all visually.

When to Use Expression Sets

  • Complex calculations — insurance premium calculations, loan interest rates, discount percentages
  • Multi-step logic — where the output of one calculation feeds into another
  • Conditional processing — different calculation paths based on input conditions
  • Combining matrix lookups with calculations — look up a base rate from a matrix, then apply multipliers and adjustments

Step-by-Step: Creating an Expression Set

Let us create an Expression Set that calculates a discount percentage based on order quantity and customer loyalty tier.

Step 1: Navigate to Expression Sets

  1. Open the OmniStudio app
  2. Click on the Expression Sets tab
  3. Click New

Step 2: Define the Expression Set

  1. Name: DiscountCalculator
  2. Version: 1

Step 3: Define Variables

Add these variables in the Expression Set designer:

Variable NameTypeDirectionDescription
OrderQuantityNumberInputThe number of items ordered
LoyaltyTierTextInputThe customer’s loyalty tier (Bronze, Silver, Gold, Platinum)
BaseDiscountNumberInternalCalculated base discount
LoyaltyMultiplierNumberInternalMultiplier from loyalty tier
FinalDiscountNumberOutputThe final discount percentage

Step 4: Build the Calculation Logic

Add elements to the canvas to build the calculation flow:

Element 1: Determine Base Discount (Conditional)

IF OrderQuantity >= 100 THEN
    BaseDiscount = 15
ELSE IF OrderQuantity >= 50 THEN
    BaseDiscount = 10
ELSE IF OrderQuantity >= 20 THEN
    BaseDiscount = 5
ELSE
    BaseDiscount = 0

Element 2: Determine Loyalty Multiplier (Matrix Lookup or Conditional)

You can either call a Decision Matrix here or use inline conditions:

IF LoyaltyTier == "Platinum" THEN
    LoyaltyMultiplier = 2.0
ELSE IF LoyaltyTier == "Gold" THEN
    LoyaltyMultiplier = 1.5
ELSE IF LoyaltyTier == "Silver" THEN
    LoyaltyMultiplier = 1.25
ELSE
    LoyaltyMultiplier = 1.0

Element 3: Calculate Final Discount

FinalDiscount = MIN(BaseDiscount * LoyaltyMultiplier, 25)

The MIN function caps the discount at 25% regardless of the calculation result.

Step 5: Test the Expression Set

  1. Click Preview
  2. Enter test inputs:
{
  "OrderQuantity": 75,
  "LoyaltyTier": "Gold"
}
  1. Expected walkthrough:

    • OrderQuantity is 75, so BaseDiscount = 10
    • LoyaltyTier is Gold, so LoyaltyMultiplier = 1.5
    • FinalDiscount = MIN(10 * 1.5, 25) = MIN(15, 25) = 15
  2. Expected output:

{
  "FinalDiscount": 15
}
  1. Activate the Expression Set

Using BRE Components

You can invoke Decision Matrices and Expression Sets from:

CallerHow
Integration ProcedureAdd a Matrix Action or Expression Set Action element
OmniScriptCall through an Integration Procedure (recommended) or directly via a remote action
ApexUse the omnistudio.DecisionMatrixService or omnistudio.ExpressionSetService classes
FlowUse an Apex invocable action that wraps the BRE service

BRE Best Practices

  • Use Decision Matrices for lookups and Expression Sets for calculations — do not try to force calculations into a matrix.
  • Keep matrices maintainable — if your matrix has more than 50 rows, consider splitting it into multiple matrices or using grouped matrices.
  • Version everything — both matrices and expression sets support versioning. Always create a new version when making changes.
  • Test edge cases — what happens at the boundary values? What if an input is null? Make sure the BRE handles these gracefully.
  • Document your logic — use the Description field on each element to explain what it does. Future maintainers will thank you.
  • Combine matrices and expressions — a common pattern is to look up a base value from a matrix, then apply adjustments and calculations in an expression set.

Section Notes

Here are some additional things to keep in mind when working with OmniStudio:

Standard vs. Managed Package Runtime

Salesforce offers two flavors of OmniStudio:

FeatureStandard (Metadata) RuntimeManaged Package (Vlocity) Runtime
DeploymentMetadata API, change sets, CI/CD pipelinesVlocity Build Tool (DataPacks)
NamespaceNo namespace — standard Salesforce metadatavlocity_cmt, vlocity_ins, or vlocity_ps namespace
Custom ObjectsUses standard OmniStudio objectsUses namespaced Vlocity objects
PerformanceOptimized for Lightning Web Components (LWR)Aura-based rendering
Future SupportActively developed and recommendedBeing phased out; migration tools available

If you are starting fresh, always use the standard runtime. If you have an existing Vlocity implementation, Salesforce provides migration tools to move to the standard runtime.

Debugging Tips

  1. OmniScript Debug Mode — when previewing an OmniScript, open the browser console. OmniStudio logs detailed information about data flow, element rendering, and action execution.
  2. Integration Procedure Logs — each execution is logged. You can view step-by-step execution details, input/output for each element, and any errors.
  3. Data Mapper Preview — always test your Data Mappers in isolation before wiring them into Integration Procedures or OmniScripts.
  4. FlexCard Debug Panel — in the FlexCard designer, the preview panel shows the raw JSON data being fed to the card.

Performance Considerations

  • Minimize data source calls — each FlexCard data source call is a server round trip. Use Integration Procedures to batch multiple queries.
  • Enable caching — FlexCards and Integration Procedures support caching. Use it for data that does not change frequently.
  • Limit child FlexCard nesting — deeply nested FlexCards can cause performance issues. Keep nesting to 2-3 levels maximum.
  • Use Turbo Extract — it is significantly faster than the legacy Extract Data Mapper.
  • Lazy load when possible — do not load all data upfront. Use conditional rendering and on-demand data fetching.

Deployment and Source Control

OmniStudio components (in the standard runtime) are stored as metadata and can be deployed using:

  • Change Sets — works but can be cumbersome for complex OmniStudio components
  • Metadata API — the most reliable method for CI/CD pipelines
  • Salesforce CLI (sf) — use sf project deploy start to push OmniStudio metadata
  • Salesforce DevOps Center — supports OmniStudio metadata deployment
  • Unlocked Packages — package your OmniStudio components for distribution

When working with source control, OmniStudio metadata is stored in the standard force-app/main/default directory structure, alongside your other Salesforce metadata.

Common Gotchas

  1. Activation order matters — you must activate components in dependency order: Data Mappers first, then Integration Procedures, then OmniScripts, then FlexCards.
  2. Version mismatches — if a FlexCard references version 2 of an Integration Procedure but only version 1 is activated, things will break silently. Always check version references.
  3. Record context — FlexCards on Lightning record pages automatically receive recordId. But when embedded in OmniScripts or other contexts, you need to pass it explicitly.
  4. Namespace issues during migration — when migrating from the managed package to standard runtime, field references may need to be updated to remove the namespace prefix.
  5. Permission sets — users need the appropriate OmniStudio permission sets to view FlexCards and run OmniScripts. Do not forget to assign them.

PROJECT: Create a FlexCard to Display Data to Users

In this project, we will build a practical FlexCard that displays a Contact’s information along with their related Account details and recent Cases. This is the kind of card a support agent might see on their console when handling an inquiry.

Requirements

  • Display the Contact’s name, email, phone, and title
  • Display the related Account name and industry
  • Show the 5 most recent Cases for this Contact
  • Include an action to create a new Case
  • Include an action to send an email to the Contact
  • Use a different state when the Contact has no Cases

Step 1: Create the Data Source

First, we need an Integration Procedure to fetch all the data in one server call.

Create the Integration Procedure: Contact.GetSupportCard

Add a DataRaptor Turbo Extract element with this configuration:

  • Primary Object: Contact
  • Fields:
FieldOutput Path
Contact.IdContactId
Contact.NameContactName
Contact.EmailEmail
Contact.PhonePhone
Contact.TitleTitle
Contact.Account.NameAccountName
Contact.Account.IndustryAccountIndustry
Contact.Cases.IdCases:CaseId
Contact.Cases.CaseNumberCases:CaseNumber
Contact.Cases.SubjectCases:Subject
Contact.Cases.StatusCases:Status
Contact.Cases.PriorityCases:Priority
Contact.Cases.CreatedDateCases:CreatedDate
  • Filter: Contact.Id = :recordId
  • Case Filter: ORDER BY CreatedDate DESC LIMIT 5

Activate the Integration Procedure.

Step 2: Create the FlexCard

  1. Create a new FlexCard named Contact_SupportCard
  2. Set the Data Source to Integration Procedure: Contact.GetSupportCard
  3. Pass recordId as the input parameter

Step 3: Design the Default State

Build the layout:

Header Section:

  • Icon: standard:contact (large, left-aligned)
  • Text (Heading): {ContactName}
  • Text (Subheading): {Title} at {AccountName}

Details Section (using a Block with columns):

Left ColumnRight Column
Email: {Email}Phone: {Phone}
Account: {AccountName}Industry: {AccountIndustry}

Cases Table Section:

Add a Datatable element with columns:

ColumnField
Case #{Cases:CaseNumber}
Subject{Cases:Subject}
Status{Cases:Status}
Priority{Cases:Priority}
Date{Cases:CreatedDate}

Actions Section:

  1. New Case — launches an OmniScript Case.CreateCase and passes ContactId and AccountName as inputs
  2. Send Email — URL action: mailto:{Email}
  3. View Account — Navigate action: /lightning/r/Account/{AccountId}/view

Step 4: Create the “No Cases” State

  1. Add a new state named NoCases
  2. Set the condition: {Cases} == null || {Cases}.length == 0
  3. Duplicate the header and details sections from the default state
  4. Replace the cases table with a Text element: “No recent cases found for this contact.”
  5. Keep the action buttons

Step 5: Style and Activate

  1. Use the Style panel to adjust padding, font sizes, and colors
  2. Set the card width to match your target layout (full width for a console sidebar, or a specific pixel width for a dashboard)
  3. Preview with several Contact records — test with contacts that have cases and contacts that do not
  4. Activate the FlexCard

Step 6: Embed the FlexCard

  1. Navigate to a Contact Lightning Record Page in Lightning App Builder
  2. Drag the OmniStudio FlexCard component into the sidebar or a section of the page
  3. Set the FlexCard property to Contact_SupportCard
  4. Save and activate the page

The FlexCard will now appear on every Contact record, showing the support agent a quick summary of who they are talking to, their account context, and their recent case history.


PROJECT: Create an OmniScript to Allow Users to Input Data

In this project, we will build a complete OmniScript that guides a support agent through the process of creating a new Case with all the relevant information. This script will validate input, look up related records, apply business rules to determine the support tier, and create the Case.

Requirements

  • Step 1: Identify the customer (look up Contact and Account)
  • Step 2: Capture case details (origin, priority, type, subject, description)
  • Step 3: Apply business rules to determine support tier and SLA
  • Step 4: Review all information and submit
  • Auto-assign the case to the correct queue based on the support tier

Step 1: Create Supporting Components

Before building the OmniScript, we need a few supporting components.

Data Mapper — Turbo Extract: Contact_LookupByEmail

  • Primary Object: Contact
  • Fields: Id, Name, Email, Phone, AccountId, Account.Name, Account.AnnualRevenue, Account.Industry
  • Filter: Email = :email

Data Mapper — Load: Case_CreateWithAssignment

  • Object: Case
  • Fields: AccountId, ContactId, Origin, Priority, Type, Subject, Description, OwnerId (for queue assignment)
  • Operation: Insert

Integration Procedure: Case.SubmitNewCase

This procedure will:

  1. Call the SupportTierMatrix Decision Matrix to determine the tier
  2. Look up the appropriate queue Id based on the tier
  3. Call the Case_CreateWithAssignment Data Mapper to create the Case
  4. Return the new Case Id to the OmniScript

Step 2: Build the OmniScript

Create a new OmniScript:

  • Type: Case
  • Sub Type: NewSupportCase
  • Language: English

Step 1: Customer Identification

Drag a Step onto the canvas. Name it CustomerInfo. Label: Customer Identification.

Add these elements:

  1. Email Input — Name: CustomerEmail, Label: “Customer Email”, Required: true
  2. Remote Action — Name: LookupContact, triggered when CustomerEmail loses focus. Calls the Contact_LookupByEmail Data Mapper with { "email": "%CustomerInfo:CustomerEmail%" }
  3. Text Display (read-only) — Name: ContactName, Label: “Contact Name”, Value: %LookupContact:Name%
  4. Text Display (read-only) — Name: AccountName, Label: “Account”, Value: %LookupContact:Account.Name%
  5. Text Display (read-only) — Name: AccountIndustry, Label: “Industry”, Value: %LookupContact:Account.Industry%
  6. Hidden Fields — Store ContactId, AccountId, and AnnualRevenue for later steps

Add a Validation message: “Please enter a valid customer email to proceed.”

Show/hide logic: the Contact and Account display fields should only appear after a successful lookup (%LookupContact:Id% != null).

Step 2: Case Details

Drag another Step onto the canvas. Name it CaseDetails. Label: Case Details.

Add these elements:

ElementNameConfiguration
DropdownCaseOriginOptions: Phone, Email, Web, Chat, Social Media. Required.
DropdownCasePriorityOptions: Low, Medium, High, Critical. Default: Medium. Required.
DropdownCaseTypeOptions: Problem, Feature Request, Question, Billing, Access Issue. Required.
Text InputCaseSubjectMax length: 255. Required. Placeholder: “Brief description of the issue”
Rich Text AreaCaseDescriptionMax length: 32000. Required. Placeholder: “Provide details about the issue…”
File UploadAttachmentsOptional. Accept: .pdf, .png, .jpg, .docx. Max files: 5.
Text AreaEscalationReasonShow only when CasePriority == “Critical”. Label: “Reason for Critical Priority”. Required when visible.

Step 3: Support Tier and SLA

Drag another Step onto the canvas. Name it SupportTier. Label: Support Tier & SLA.

This step runs an Integration Procedure Action on entry (before the user sees the step) to determine the support tier:

  1. Add an Integration Procedure Action element (set to run on Step Entry)
  2. Call the Case.DetermineSupportTier Integration Procedure
  3. Pass inputs:
{
  "AnnualRevenue": "%CustomerInfo:AnnualRevenue%",
  "Industry": "%CustomerInfo:AccountIndustry%",
  "CasePriority": "%CaseDetails:CasePriority%"
}
  1. The Integration Procedure calls the SupportTierMatrix Decision Matrix and returns:
{
  "SupportTier": "Gold",
  "ResponseTimeSLA": 8,
  "AssignedQueueId": "00Gxx000000ABCDE"
}

Display the results to the user:

ElementValue
Text Block (styled)“Based on the customer’s account and case priority, this case qualifies for {SupportTier} support.”
Text Block”Response Time SLA: {ResponseTimeSLA} hours
Text Block”The case will be assigned to the {SupportTier} Support Queue.”

This step is primarily informational — it shows the agent what tier the customer falls into before they submit.

Step 4: Review and Submit

Drag a final Step onto the canvas. Name it ReviewSubmit. Label: Review & Submit.

Add a Formula element or Display Text elements that summarize all collected information:

Customer: %CustomerInfo:ContactName% (%CustomerInfo:CustomerEmail%)
Account: %CustomerInfo:AccountName%
Support Tier: %SupportTier:SupportTier%
SLA: %SupportTier:ResponseTimeSLA% hours

Case Origin: %CaseDetails:CaseOrigin%
Priority: %CaseDetails:CasePriority%
Type: %CaseDetails:CaseType%
Subject: %CaseDetails:CaseSubject%

Description:
%CaseDetails:CaseDescription%

Add a Checkbox element: “I have verified all information is correct” — required to proceed.

Add an Integration Procedure Post Action as the submit action:

  • Calls Case.SubmitNewCase
  • Passes all the collected data
  • On success, displays a confirmation message with the new Case Number and a link to the Case record

Step 3: Configure Navigation and Error Handling

  1. Enable Save for Later in the OmniScript properties so agents can pause and resume
  2. Add a Cancel button that asks for confirmation before discarding the script
  3. Configure Error Handling on the submit action:
    • On success: Show a confirmation step with the Case Number and a “Go to Case” button
    • On failure: Show an error message and allow the user to go back and fix the data

Step 4: Test the Complete Flow

  1. Click Preview in the OmniScript designer
  2. Walk through the entire flow:
    • Enter a customer email and verify the lookup works
    • Fill in case details with different priority levels (test the conditional escalation reason field)
    • Verify the support tier calculation is correct
    • Review the summary and submit
  3. Check the debug panel for the complete JSON payload
  4. Verify the Case was created correctly in Salesforce
  5. Test error scenarios: invalid email, missing required fields, API timeout

Step 5: Activate and Deploy

  1. Activate the OmniScript
  2. Place it on a Lightning page or add it as a FlexCard action
  3. Test with real users in a sandbox environment before deploying to production

The Complete Architecture

Here is how all the pieces fit together in this project:

User interacts with OmniScript
    |
    v
Step 1: Customer email entered
    |
    v
Data Mapper (Turbo Extract) --> Looks up Contact/Account
    |
    v
Step 2: Case details captured
    |
    v
Step 3: Integration Procedure called
    |-> Decision Matrix (SupportTierMatrix) --> Returns tier & SLA
    |-> Queue lookup --> Returns queue Id
    |
    v
Step 4: Review and Submit
    |
    v
Integration Procedure (Case.SubmitNewCase)
    |-> Data Mapper (Load) --> Creates Case record
    |-> Returns Case Id and Number
    |
    v
Confirmation displayed to user

This architecture is the real power of OmniStudio — each component does one thing well, and they compose together into a cohesive experience. The FlexCard shows existing data, the OmniScript guides data entry, the Data Mappers handle CRUD operations, the Integration Procedures orchestrate server-side logic, and the Business Rules Engine makes decisions. All without writing a single line of Apex.


Wrapping Up

OmniStudio is one of the most powerful declarative toolsets in the Salesforce ecosystem. It takes time to learn all the components and how they fit together, but once you do, you can build sophisticated, enterprise-grade experiences in a fraction of the time it would take with custom code. The key is to understand each component’s role:

  • FlexCards display data
  • OmniScripts collect data
  • Data Mappers move data in and out of Salesforce
  • Integration Procedures orchestrate everything server-side
  • Business Rules Engine makes decisions

Start small — build a simple FlexCard and a basic OmniScript. Then gradually add Integration Procedures and Decision Matrices as your requirements grow. Before you know it, you will be building complex guided experiences that used to take weeks of Apex and LWC development.

In the next part of this series, Part 28: User Authentication and SSO in Salesforce, we will dive into how to secure access to your Salesforce org with Single Sign-On, Multi-Factor Authentication, login flows, and connected apps. Stay tuned.