Skip to content

Update Live Workflow

Joseph Huckaby edited this page Aug 9, 2026 · 1 revision

Update a Live Workflow

Important

This feature requires xyOps v1.0.89 or newer.

The update_active_job API can modify a workflow while it is running. This makes it possible for one workflow job to change how future nodes will run, based on anything the current job has learned.

Update the Workflow from Inside a Job

Code running inside a workflow job can update its parent workflow directly. This example "retargets" every unlaunched Event and Job node in the current parent workflow, to a specified list of new targets.

It assumes you are using the Shell Plugin, and it relies on the xyOps SDK for Node.js which is pre-installed with both xyOps and xySat (so you can use it without having to install anything).

#!/usr/bin/env node

const { api, job } = require('@pixlcore/xyops-sdk');
const targets = ['YOUR_TARGET_ID'];

(async function() {
	try {
		await job.read();
		const parentJobID = job.workflow.job;
		
		let result = await api.getJob({ id: parentJobID });
		if (result.err) throw result.err;
		
		const workflow = result.data.job.workflow;
		const nodes = workflow.nodes.filter(function(node) {
			return ((node.type == 'event') || (node.type == 'job')) && !workflow.state[node.id];
		});
		nodes.forEach(function(node) {
			node.data.targets = targets;
		});
		
		result = await api.updateActiveJob({
			id: parentJobID,
			workflow: { nodes: workflow.nodes }
		});
		if (result.err) throw result.err;
		
		job.finalSuccess('Retargeted ' + nodes.length + ' future workflow node(s).');
	}
	catch (err) {
		job.finalError(1, err.message || String(err));
	}
})();

Replace YOUR_TARGET_ID with your server or group target ID. The current job must be running inside a workflow so job.workflow.job contains the parent Job ID.

The job also needs a Secret Vault variable named XYOPS_API_KEY, created from an API Key with the Update Jobs privilege. The SDK automatically uses the job's conductor URL and API Key for both calls.

The script fetches the live parent workflow and checks every node. An Event or Job node without an entry in workflow.state has not launched yet, so the script replaces its targets. It then submits the complete nodes array. Other workflow properties, including connections and runtime state, are preserved by the API's shallow workflow merge.

Build a Reusable Action Plugin

For a reusable version, we will build a Retarget Workflow Nodes Action Plugin. You can attach it to an Event or Job node in a workflow, enter a comma-separated list of future Workflow Node IDs, select one or more targets, and update all of those nodes before they launch.

For example, one Retarget Workflow Nodes action running on the current job can update all of these future nodes at once:

  • Node #nk3m7x2q
  • Node #n8v4p2az
  • Node #nt6c9w5r

The listed nodes do not need to be directly connected to the current node. They can be anywhere later in the workflow, as long as they have not started yet and their type is event or job.

How It Works

An Action Plugin receives a JSON document on STDIN. For job actions, xyOps includes the complete JobHookData object. The current sub-job is available in the top-level job property.

For a job running inside a workflow, job.workflow.job contains the parent workflow's live Job ID. This is the only workflow location value the plugin needs because the destination nodes are supplied explicitly in its parameters.

The plugin also receives two custom parameters:

Parameter Description
node_ids A comma-separated list of Workflow Node IDs to update.
targets The new target array for all listed nodes.

The plugin uses these values to perform the following steps:

  1. Parse the Node IDs in params.node_ids.
  2. Fetch the parent workflow job with the xyOps SDK and get_job.
  3. Locate every requested Node ID in workflow.nodes.
  4. Replace each node's data.targets array with the selected targets.
  5. Submit the complete workflow.nodes array through the SDK and update_active_job.

The workflow property has special shallow-merge behavior in update_active_job. Sending workflow.nodes does not replace workflow.connections, workflow.state, workflow.jobs, or any other omitted workflow properties. However, nodes is itself an array, so the plugin must fetch and submit the complete array.

Step 1: Create an API Key

The Action Plugin needs an API Key to call get_job and update_active_job on the local conductor.

  1. Open API Keys in the xyOps sidebar.
  2. Click New API Key.
  3. Give it a clear title, such as Retarget Live Workflow.
  4. Grant it the Update Jobs privilege.
  5. Save the API Key.
  6. Copy the generated key value immediately.

The API Key secret is only shown once, so keep it handy for the next step.

Tip

Use a dedicated API Key for this plugin, and only grant it the Update Jobs privilege.

Step 2: Create a Secret Vault

Store the API Key in a Secret Vault so it never needs to appear in the plugin source code or parameters.

  1. Open Secrets in the xyOps sidebar.
  2. Click New Vault.
  3. Give it a title, such as Live Workflow Credentials.
  4. Add the following secret variable:
Field Value
Variable Name XYOPS_API_KEY
Variable Value The API Key copied in Step 1
  1. Save the vault.

You can leave Plugin Access empty for now. We will assign the plugin after creating it.

Step 3: Create the Action Plugin

Now create the reusable Action Plugin.

  1. Open Plugins in the xyOps sidebar.
  2. Click New Plugin.
  3. Set Plugin Title to Retarget Workflow Nodes.
  4. Check Plugin Enabled.
  5. Set Type to Action Plugin.
  6. Set Executable to: node.
  7. Click Edit Script and paste in the following code:
// Retarget Workflow Nodes
const { api } = require('@pixlcore/xyops-sdk');

let inputJSON = '';
process.stdin.setEncoding('utf8');
process.stdin.on('data', function(chunk) {
	inputJSON += chunk;
});
process.stdin.on('end', async function() {
	try {
		const input = JSON.parse(inputJSON);
		const nodeIDs = input.params.node_ids.split(/\s*,\s*/);
		const parentJobID = input.job.workflow.job;
		
		let result = await api.getJob({ id: parentJobID });
		if (result.err) throw result.err;
		
		const workflow = result.data.job.workflow;
		const nodes = workflow.nodes.filter(function(node) {
			return nodeIDs.includes(node.id);
		});
		
		nodes.forEach(function(node) {
			node.data.targets = input.params.targets;
		});
		
		result = await api.updateActiveJob({
			id: parentJobID,
			workflow: { nodes: workflow.nodes }
		});
		if (result.err) throw result.err;
		
		console.log(JSON.stringify({
			xy: 1,
			code: 0,
			description: 'Retargeted workflow nodes: #' + nodes.map(function(node) { return node.id; }).join(', #')
		}));
	}
	catch (err) {
		console.log(JSON.stringify({
			xy: 1,
			code: 1,
			description: err.message || String(err)
		}));
	}
});

The xyOps SDK is pre-installed on the conductor, so this script does not require an NPM install or a separate plugin dependency.

Step 4: Add the Plugin Parameters

Still on the plugin edit page, add the following two parameters.

Parameter 1: Workflow Node IDs

Field Value
Param ID node_ids
Label Workflow Node IDs
Control Type Text Box
Required Checked
Caption Enter one or more future Event or Job Node IDs, separated by commas.

Enter values using a comma-separated format such as:

nk3m7x2q, n8v4p2az, nt6c9w5r

Whitespace surrounding each ID is ignored.

Parameter 2: Targets

Field Value
Param ID targets
Label Targets
Control Type System Menu
System Menu Targets
Multi-Select Menu Checked
Required Checked
Caption Select the new targets for all listed workflow nodes.

The Targets system menu combines all valid server groups and individual servers. With Multi-Select Menu checked, the selected values are passed to the plugin as an array in input.params.targets.

Save the plugin after adding both parameters.

For reference, the resulting parameter definitions are equivalent to:

[
	{
		"id": "node_ids",
		"title": "Workflow Node IDs",
		"type": "textarea",
		"value": "",
		"caption": "Enter one or more future Event or Job Node IDs, separated by commas.",
		"required": true
	},
	{
		"id": "targets",
		"title": "Targets",
		"type": "system",
		"list_id": "targets",
		"caption": "Select the new targets for all listed workflow nodes.",
		"multiple": true,
		"required": true
	}
]

Step 5: Assign the Secret Vault

Now give the plugin access to the API Key secret.

  1. Open Secrets.
  2. Edit the Live Workflow Credentials vault created earlier.
  3. Find Plugin Access.
  4. Select the Retarget Workflow Nodes plugin.
  5. Save the vault.

xyOps exports the vault variable as XYOPS_API_KEY when the plugin runs. It also automatically exports XYOPS_BASE_URL for Action Plugins. The SDK reads both variables during startup and handles authentication and URL construction for us.

Step 6: Add It to a Workflow

Create or edit a workflow containing the future Event or Job nodes you want to retarget.

  1. Edit each future node and copy the Node ID displayed in the dialog title.
  2. Add an Action node and select the Retarget Workflow Nodes Action Plugin.
  3. Connect the Action node to the Event or Job whose result should trigger the update.
  4. Select the desired action condition, such as Success, Error, or Complete.
  5. Enter the future Node IDs in Workflow Node IDs, separated by commas.
  6. Select the new server groups or servers in Targets.
  7. Save the workflow.

The action condition only controls when the plugin runs. It does not affect which nodes are selected. The explicit node_ids parameter makes the destination list clear, even when the workflow contains multiple branches or several wire conditions.

Note

The plugin is designed for workflow Event and Job nodes. A standalone plugin test does not contain job.workflow.job, so the full behavior must be tested in a small workflow.

Step 7: Run a Test Workflow

Before using this in production, make a small workflow with one current job and two future jobs:

  1. Configure the current node to finish successfully after a short delay.
  2. Configure both future nodes with recognizable original targets.
  3. Copy the two future Workflow Node IDs into the Action Plugin's Workflow Node IDs field.
  4. Select a different safe target in the action's Targets menu.
  5. Run the workflow.
  6. Open the Action result and confirm that it reports both updated Node IDs.
  7. Open each future sub-job after it runs and confirm that its target list contains the newly selected target.

The workflow definition itself is not changed. Only the live workflow Job record is updated for that one run, so the next run starts with the targets saved in the original workflow definition.

Choosing Nodes and Targets from Earlier Results

The reusable parameters are configured independently on each Action node. This makes it easy to select different node lists and targets based on the current job's result:

  • Add one Retarget Workflow Nodes Action on the Success condition, listing the nodes and targets for the successful path.
  • Add another on the Error condition, listing diagnostic nodes or fallback targets.
  • Use Complete when the same retargeting should happen regardless of the current job's result.

For more advanced rules, you can extend the script to inspect any data included in input.job, including result codes, descriptions, tags, and workflow data, before choosing the final Node IDs or targets.

Why This Works for a Running Workflow

Standard remote jobs are owned by xySat while they execute. xySat sends full Job updates back to the conductor, so update_active_job rejects those jobs while their job.remote property is set.

This plugin does not update the remote sub-job. It reads the parent ID from job.workflow.job and updates the top-level workflow Job instead. The parent workflow remains owned by the conductor for its entire run, so its future node definitions can be safely changed while a sub-job is completing.

Troubleshooting

Missing XYOPS_API_KEY

The Secret Vault is not assigned to the Action Plugin, or the variable name does not exactly match XYOPS_API_KEY. Edit the vault, check Plugin Access, and verify the variable name.

This Action Must Run Inside a Workflow

The action did not receive job.workflow.job. Make sure it is attached to an Event or Job node inside a workflow. This error is expected if you use the standalone Action Plugin test dialog.

Invalid Workflow Node ID

Workflow Node IDs are eight-character lowercase alphanumeric strings beginning with n. Enter a comma-separated list such as nk3m7x2q, n8v4p2az, without labels, quotes, or hash symbols.

Workflow Node Not Found

One of the IDs does not exist in the live workflow. Check for a typo and make sure you copied the Node ID from the same workflow definition.

Workflow Node Is Not an Event or Job Node

The listed ID belongs to an Action, Limit, Controller, Trigger, or Note node. This example only changes target arrays on event and job nodes.

Workflow Node Has Already Started

The action ran too late to safely change one of the listed nodes. Move the action earlier in the workflow or remove that Node ID from the list.

Permission Denied

Verify that the API Key has the Update Jobs privilege.

Active Job Not Found

The parent workflow completed before the API call arrived, or the wrong Job ID was supplied. The plugin must call update_active_job with job.workflow.job, not the current sub-job's job.id.

Going Further

The same API can make other changes to future workflow nodes. For example, an Action Plugin can change future parameters, algorithms, tags, labels, or replay settings.

It can even add new nodes while a workflow is running. To do this, fetch the live workflow, append new entries to the complete workflow.nodes array, append the required wires to the complete workflow.connections array, and submit both arrays:

const response = await api.updateActiveJob({
	id: parentJobID,
	workflow: {
		nodes: completeUpdatedNodes,
		connections: completeUpdatedConnections
	}
});

Each new node needs a unique lowercase alphanumeric ID, valid x and y coordinates, and data appropriate for its node type. Each new connection needs its own unique ID plus valid source and dest Node IDs. Most importantly, the new connections must make the node reachable from workflow execution that has not happened yet.

Clone this wiki locally