Skip to main content

First Workflows

You now have a provisioned project. This lesson is the first hands-on step: you will create all the example workflow definitions, then run one and inspect its result. This introduces the create → run → monitor cycle you will repeat for every task type in the rest of the course.

All the functions in this lesson live in the weather_workflow_demo_static script. Open it in the Twinit IDE Extension; its runnable functions appear in the script's run menu, grouped under headings (workflow creation, management, monitoring, and the underlying task scripts).

Step 1: Create the workflow definitions#

Read the Code#

Let's take a look at the basic code for creating a Workflow Definition.

Open the weather_workflow_demo_static script from Twinit. Search for "WORKFLOW 1: Basic Weather Fetch".

In this function you can see the basics of creating a workflow. First we define the Workflow Definition.

const workflowDef = {  _name: "Basic_Weather_Fetch_Workflow",  _description: "Fetch weather for multiple cities sequentially (replaces orchestrator pattern)",  _namespaces: ctx._namespaces,  _userType: "weather_workflow_basic_static",  _timeoutPolicy: "TIME_OUT_WF",  _timeoutSeconds: 300,  _taskDefs: [      {          _type: "SCRIPT_EXECUTION",          _name: "fetch_newyork_weather",          _sequenceno: 1,          _retryCount: 2,          _timeoutSeconds: 30,          _inputParams: {              _userType: "weather_workflow_demo_static",              _scriptName: "generateNewYorkWeatherData"          }      },      {          _type: "SCRIPT_EXECUTION",          _name: "store_newyork_weather",          _sequenceno: 2,          _retryCount: 2,          _timeoutSeconds: 60,          _inputParams: {              _userType: "weather_workflow_demo_static",              _scriptName: "parseAndStoreWeatherFromWorkflow",              "weatherData": "${fetch_newyork_weather._output.scriptOutput}"          }      },      {          _type: "SCRIPT_EXECUTION",          _name: "fetch_sanfrancisco_weather",          _sequenceno: 3,          _retryCount: 2,          _timeoutSeconds: 30,          _inputParams: {              _userType: "weather_workflow_demo_static",              _scriptName: "generateSanFranciscoWeatherData"          }      },      {          _type: "SCRIPT_EXECUTION",          _name: "store_sanfrancisco_weather",          _sequenceno: 4,          _retryCount: 2,          _timeoutSeconds: 60,          _inputParams: {              _userType: "weather_workflow_demo_static",              _scriptName: "parseAndStoreWeatherFromWorkflow",              "weatherData": "${fetch_sanfrancisco_weather._output.scriptOutput}"          }      }  ]};

Three things to notice abotu Workflow Definitions:

  1. The _name, _description, and _usertype pattern should be familiar now.
  2. The Worfklow Def also includes two timeout configurations. The default _timeoutPolicy: "TIME_OUT_WF" will terminate a workflow and mark it as TIMED_OUT after _timeoutSeconds: 300.
  3. The _taskDefs array contains the ordered list of tasks that workflow will complete.
{  _type: "SCRIPT_EXECUTION",  _name: "store_newyork_weather",  _sequenceno: 2,  _retryCount: 2,  _timeoutSeconds: 60,  _inputParams: {      _userType: "weather_workflow_demo_static",      _scriptName: "parseAndStoreWeatherFromWorkflow",      "weatherData": "${fetch_newyork_weather._output.scriptOutput}"  }}

Three things to notice about tasks, all of which recur throughout the course:

  1. _sequenceno orders the tasks. Tasks run in ascending sequence.
  2. A SCRIPT_EXECUTION task names the script to run via _userType + _scriptName. Here _userType: "weather_workflow_demo_static" points at this very script; _scriptName selects the function.
  3. Data flows by binding. store_newyork_weather reads the output of fetch_newyork_weather through ${fetch_newyork_weather._output.scriptOutput}. The fetch step returns a forecast object; the store step receives it as weatherData and writes readings into the telemetry collection.

The fetch scripts (generateNewYorkWeatherData, generateSanFranciscoWeatherData) return static forecast JSON shaped exactly like a live weather API response, so the store script is identical to what it would be in a live production implementation.

Once the Workflow Definiition is defined it is created using the IafWorkflowSvc api.

const result = await IafWorkflowSvc.createWorkflowDef(workflowDef, ctx);

Run the Code#

Run createAllWeatherWorkflows.

This single function creates all nine example WorkflowDefs in order. Internally it iterates over a list (ALL_WEATHER_WORKFLOWS) and calls each definition's creator function:

_userTypeCreatorWhat it demonstrates
weather_workflow_basic_staticcreateBasicWeatherFetchWorkflowSequential steps
weather_workflow_forkjoin_staticcreateParallelWeatherFetchWorkflowFORK_JOIN parallelism
weather_workflow_router_staticcreateWeatherRouterWorkflowSWITCH routing
weather_workflow_enricher_staticcreateWeatherEnricherWorkflowEnricher pattern
weather_workflow_splitter_staticcreateWeatherSplitterAggregatorWorkflowSplitter–aggregator
weather_workflow_polling_staticcreateWeatherPollingWorkflowDO_WHILE polling
weather_workflow_transformer_staticcreateWeatherTransformerWorkflowMessage translation
weather_workflow_userinput_staticcreateWeatherUserInputWorkflowUSER_INPUT
weather_workflow_ai_converse_staticcreateWeatherAIConverseWorkflowAI_CONVERSE

By default any previous definition with the same _userType is deleted first. This makes the function safe to re-run. You can call it again at any point in the course to reset all definitions to a known state.

The result summarises what happened:

{    totalRequested: 9,    totalCreated: 9,    totalFailed: 0,    results: [ { userType, label, success, workflowDefId, workflowName, workflowDef }, ... ]}

Each entry includes the full server-returned workflowDef, so you can confirm your _taskDefs round-tripped through validation intact. If a definition fails validation, its entry has success: false and an error string. A fast way to catch a malformed task before you try to run it.

If you prefer to create definitions one at a time, run any single creator (e.g. createBasicWeatherFetchWorkflow) instead. The bulk function is just a convenience wrapper.

You can also see your Workflow Definitions in the IDE Extensions Workflow Service panel.

workflow service panel

Step 2: Run a Workflow#

Run runWeatherWorkflow with input pasting the JSON below into the input field:

{ "workflowUserType": "weather_workflow_basic_static" }

runWeatherWorkflow looks the definition up by _userType, then starts a run. It is worth reading how it starts the run, because the SDK has a sharp edge here:

const runBody = {    _workflowDefId: workflow._id,   // REQUIRED on the body    _inputParams: inputParams || {}};const result = await IafWorkflowSvc.runWorkflow(workflow._id, runBody, ctx);

The REST contract for POST /workflowsvc/api/v1/workflows requires _workflowDefId in the body. The SDK's runWorkflow(workflowDefId, ...) takes the id as its first argument but does not add it to the request for you, so you must include it in runBody yourself. Omitting it returns HTTP 400. Keep this in mind whenever you start a workflow from your own code.

The function returns the started run's id and status:

{ workflowId, workflowName, runResult: { _id, _status, ... } }

Step 3: Check the result#

A workflow run is asynchronous. Two helpers let you observe it:

  • getLatestWorkflowRunByUserType with { "workflowUserType": "weather_workflow_basic_static" } returns the most recent run for a definition, including per-task status and results. Use this when you just want the latest outcome.

  • pollWorkflowUntilComplete with { "workflowId": "<run id from step 3>" } polls until the run reaches a terminal state (COMPLETED, FAILED, TIMED_OUT, TERMINATED) or the poll budget is exhausted, then returns the final status and each task's result.

A completed basic run shows four tasks in COMPLETED status. The two store tasks report how many readings they wrote. You can confirm the data landed by running fetchSensorReadingsPerSourceId (from the weather_feature script), which reads the telemetry readings back out per site.

The create → run → monitor cycle#

Every remaining lesson follows the same shape:

  1. Create the definition (already done by createAllWeatherWorkflows, or re-run the individual creator to study it).
  2. Run it with the IDE Extension or runWeatherWorkflow (some workflows take runtime inputs).
  3. Monitor it with the IDE Extension or getLatestWorkflowRunByUserType or pollWorkflowUntilComplete, and inspect task-level detail when needed.

There is also runAllWeatherWorkflows, which starts every definition in one call (optionally polling each to completion). It is convenient once you understand the individual workflows, but work through them one at a time first. That is what the next lessons do.