Use a chain.json file to define an ordered graph of nodes to create, update, or delete the templated app assets in the subscriber org. Your chain files act as the installation script for the template, making sure that the correct assets exist based on the subscriber org and on app and asset dependencies. A node is an entry in definition.nodes, and its graph node type, such as DashboardUpsert, is the task it runs.
Learn how to add your chains to template-info.json, structure your chain files, choose node types, and meet security review requirements.
Add Chains to Your Template
Each template requires a Create chain. OrgReadiness, Update, and Delete chains are optional. Without update or delete chains, the framework updates or deletes all template assets. Use a readiness chain when you need to check the subscriber org for prerequisites before creating or updating the templated app. To stop the templated app from being created or updated when a readiness check fails, fail the task. The log messages explain why the process failed.
Name the files anything you like, and map each one to its correct type in chainDefinitions.
Chain files contain the chain metadata and the definition of one or more nodes.
Each node definition has required graphNodeType, parameters, and sources attributes, and an optional runAs attribute. A root node has an empty sources array. Every other node lists at least one node it depends on, and any node in the chain can be a source.
Each chain.json file has these attributes:
Attribute
Path
Type
Required
Description
name
(top-level)
String
Yes
A unique name for the chain.
label
(top-level)
String
No
A label for the chain.
dominoVariant
(top-level)
String
Yes
The framework variant for the chain. For all chain files, this value must be sfdc_internal__UnifiedAnalyticsDominoVariant.
parameters
(top-level)
Object
Yes
A collection of parameters for the chain. This is a placeholder for allowing chain-level payload attributes.
definition
(top-level)
Object
Yes
The definition of ordered nodes for the chain.
nodes
definition
Object
Yes
A collection of one or more named nodes for the chain to run.
<node_name>
definition.nodes
Object
Yes
A unique key for the node object. Child nodes use this key in their sources attribute.
A collection of parameters for the node type. See each node type reference for the specific parameters.
sources
definition.nodes.<node_name>
Array
Yes
The parent sources for this node, if any. For a root node with no dependencies, use an empty array, []. For a child node, the array must list the <node_name> of each node that the child node depends on.
runAs
definition.nodes.<node_name>
String
No
The user permissions for running the node. Valid values are CurrentUser and AutoProc. For most nodes, use CurrentUser, which runs the node as the user installing the app. For the ContentAssetUpsert node type, use AutoProc, which runs as a system user. Delete chains require CurrentUser. See Best Practices. The default value is AutoProc.
When node parameters include file attributes, the file paths resolve from the root of the template bundle. This sample bundle contains the files that the chain examples use:
This example shows a chain file that uses sources dependencies to run multiple nodes in order. First, it creates a workspace, then a semantic model for the workspace, and then a visualization that uses the semantic model as a data source. The order matters. For example, don’t create a visualization before its supporting semantic model exists. The chain.json file encodes the sequencing explicitly.
Asset names use app context references, ${App.<AssetType>.<name>.Name}, so they’re unique to each installed app.
When WorkspaceUpsert creates a workspace, it can name the assets that the workspace contains, such as a semantic model.
Note
1{2 "name": "create_app",3 "label": "Create app with a workspace, semantic model, and visualization",4 "dominoVariant": "sfdc_internal__UnifiedAnalyticsDominoVariant",5 "parameters": {},6 "definition": {7 "nodes": {8 "upsert_workspace": {9 "graphNodeType": {10 "name": "WorkspaceUpsert",11 "label": "Create the workspace with a reference to the semantic model"12},13 "parameters": {14 "name": "${App.Workspaces.MyWorkspace.Name}",15 "label": "My Workspace",16 "file": "workspaces/myWorkspace.json",17 "assets": [18{19 "name": "${App.SemanticModels.MySemanticModel.Name}",20 "assetType": "SemanticModel",21 "usageType": "Referenced"22}23]24},25 "sources": [],26 "runAs": "CurrentUser"27},28 "upsert_sm": {29 "graphNodeType": {30 "name": "SemanticModelUpsert",31 "label": "Create the semantic model"32},33 "parameters": {34 "name": "${App.SemanticModels.MySemanticModel.Name}",35 "label": "My Semantic Model",36 "file": "sms/mySemanticModel.json",37 "capabilities": {38 "editLocked": "true"39}40},41 "sources": ["upsert_workspace"],42 "runAs": "CurrentUser"43},44 "upsert_visualization": {45 "graphNodeType": {46 "name": "VisualizationUpsert",47 "label": "Create the visualization"48},49 "parameters": {50 "name": "${App.Visualizations.MyVisualization.Name}",51 "label": "My Visualization",52 "file": "visualizations/myVisualization.json",53 "capabilities": {54 "editLocked": "true"55}56},57 "sources": ["upsert_sm"],58 "runAs": "CurrentUser"59}60}61}62}
Readiness Chain Example
This example shows a readiness chain that performs four tasks:
readiness_check1 verifies that the required org preferences are present
template_requirement verifies that those preferences have the expected values
readiness_check2 counts the rows in a Data 360 object
template_requirement2 verifies that the row count is greater than 0
The readiness_check2 node sets valueOnError to -1. If the count fails, the node returns WARN_AND_CONTINUE, and template_requirement2 then fails the chain with a clear message.
1{2 "name": "perform_readiness_checks",3 "label": "Performs Readiness Checks",4 "dominoVariant": "sfdc_internal__UnifiedAnalyticsDominoVariant",5 "parameters": {},6 "definition": {7 "nodes": {8 "readiness_check1": {9 "graphNodeType": {10 "name": "OrgPreferenceReadiness",11 "label": "Verify that org preferences exist"12},13 "parameters": {14 "name": "OrgPrefCheck",15 "preferenceNames": ["${Variables.preferenceToCheck1}", "${Variables.preferenceToCheck2}"]16},17 "sources": [],18 "runAs": "CurrentUser"19},20 "template_requirement": {21 "graphNodeType": {22 "name": "TemplateRequirementEvaluation",23 "label": "Verify the org preferences have the expected values"24},25 "parameters": {26 "condition": "${Readiness['OrgPrefCheck'][Variables.preferenceToCheck1]}",27 "expression": "${Readiness['OrgPrefCheck'][Variables.preferenceToCheck2] == Variables.expectedValue}",28 "successMessage": "Congratulations! The expected org preference [${Variables.preferenceToCheck2}] value matches!",29 "failMessage": "Expected preference [${Variables.preferenceToCheck2}] value does not match. Expected: ${Variables.expectedValue}, Actual: ${Readiness['OrgPrefCheck'][Variables.preferenceToCheck2]}"30},31 "sources": ["readiness_check1"],32 "runAs": "CurrentUser"33},34 "readiness_check2": {35 "graphNodeType": {36 "name": "DataCloudRowCountReadiness",37 "label": "Verify there is an Id row in the Data 360 object"38},39 "parameters": {40 "name": "IdFieldRowCountCheck",41 "object": "${Variables.dataCloudObject}",42 "filters": [43{44 "field": "Id__c",45 "operator": "NotEqual",46 "value": ""47}48],49 "valueOnError": -150},51 "sources": ["template_requirement"],52 "runAs": "CurrentUser"53},54 "template_requirement2": {55 "graphNodeType": {56 "name": "TemplateRequirementEvaluation",57 "label": "Verify the ID row count is greater than 0"58},59 "parameters": {60 "expression": "${Readiness.IdFieldRowCountCheck > 0}",61 "successMessage": "Congratulations! The expected Data 360 object has data!",62 "failMessage": "Expected Data 360 object doesn't have data."63},64 "sources": ["readiness_check2"],65 "runAs": "CurrentUser"66}67}68}69}
Graph Node Types
Graph node types are the tasks you want your chain to run. Tasks include creating, updating, and deleting assets, and running readiness checks. Each supported graph node type has specific parameters. For parameters and example code, see the reference for each graph node type.
Creates or updates a Tableau Next workspace for a templated app.
Handle Task Failures
Depending on the node type, a failed task either stops the chain or logs a warning and lets the chain continue. Each node type reference lists its failure type.
The chain continues. If the node defines valueOnError, that value is written to the app context for downstream nodes.
Best Practices and Security Review Requirements
Follow these practices so that your template installs safely and passes security review.
Best Practices
Use ${App.<AssetType>.<name>.Name}, ${App.Dataspace.Name}, or ${Variables.x} for node parameters instead of hardcoded org-specific IDs and names.
Never hardcode asset names, such as "name": "BumpChart", in chains. Asset names resolve globally within the workspace and aren’t scoped to your template. A name collision can let your template overwrite or delete another installed template’s assets. Qualify names with the app ID or your namespace.
Security Review Requirements
Using runAs: "AutoProc" on any node in a delete-chain.json is an automatic fail because it grants irreversible, system-level deletion power that isn’t scoped to what the installing user owns. Always set runAs: "CurrentUser" in all nodes in delete chains. If you omit runAs, the node runs as AutoProc, which fails security review in delete chains.
Using FlowDefinitionUpsert nodes triggers a mandatory manual security review. A packaged Flow can call Apex outside your namespace, perform CRM DML, or make callouts, all fully under your control as the template developer. If you include a Flow, be ready to document everything it does.
Using TemplateDirectRest nodes with any ${Variables.*} reference in the URL, endpoint, or host is an automatic fail because it’s a server-side request forgery (SSRF) risk. Disclose any hardcoded URLs in your template submission so that the reviewer can confirm that you own the domain.