---
title: "Using the Workflow Service to approve new or updated data"
slug: "using-the-workflow-service-to-approve-new-or-updated-data"
updated: 2026-01-21T16:50:08Z
published: 2026-01-21T16:50:08Z
canonical: "support.lusid.com/using-the-workflow-service-to-approve-new-or-updated-data"
---

> ## Documentation Index
> Fetch the complete documentation index at: https://support.lusid.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Using the Workflow Service to approve new or updated data

In this tutorial we'll see how to use the [Workflow REST API](https://www.lusid.com/docs/api/workflow/intro) to model a workflow for a simple approvals process.

Let's imagine we want to model the process of approving some data. To do this, we first create a task definition that models the possible flows. We can then create a task each time we want to actually approve some data.

Note we'll only transition between states manually in this tutorial. To learn how to automate workflows, [follow this tutorial on setting up a data quality control to check new pricing data for outliers](/v1/docs/setting-up-a-data-quality-control-to-check-new-pricing-data-for-outliers).

![](https://cdn.document360.io/d575ad81-c0ed-4980-bbd1-d59ac5c3de82/Images/Documentation/720be642-8846-4ec4-a7d6-dabe9198d463.png)

> [!NOTE]
> **Note**: To complete this tutorial, you must have suitable access control permissions. This can most easily be achieved by assigning your LUSID user the built-in `lusid-administrator` role, which should already be the case if you are the domain owner. If you are informed you do not have a license to perform a certain operation, [contact support](https://www.finbourne.com/contact).

## Step 1: Creating a task definition to model the workflow

We must first create a [task definition](/v1/docs/how-do-i-create-a-task-definition) using the [CreateTaskDefinition](https://www.lusid.com/docs/api/workflow/endpoints/task-definitions/CreateTaskDefinition) API, passing in:

- An API access token as a Bearer token in the Authorization HTTP header. [See how to obtain a token](/v1/docs/how-do-i-obtain-and-use-a-short-lived-api-access-token-from-okta).
- A `scope` and `code` that together uniquely identify the task definition.
- A `displayName` and `description` for the task definition.
- A set of possible `states`; in our example we need `Pending`, `Approved`, `Denied` and `NotRequired`.
- A `fieldSchema` defining a set of fields, for our example we need `assignee`, `dataToApprove` and `reasonForDecision` as fields.
- An `initialState` of `Pending`, and a `dataToApprove` field that is required to enter this state.
- A set of `triggers` to prompt state transitions. Each trigger should contain the following:
  - A `name` for the trigger.
  - The `type` of the trigger - for now only `External` triggers are available.
- A set of `transitions` to control movement from the initial state to all the other states. Each transition should contain the following:
  - A `fromState` specifying the state the transition begins in.
  - A `toState` specifying the state the transition ends in.
  - The name of the `trigger` prompting the transition to occur.
  - Optionally, a `guard` condition which must be met for the transition to succeed. [Read more on using guards in workflows.](/v1/docs/using-guards-to-add-conditions-to-workflow-state-transitions)

> [!NOTE]
> **Note**: To avoid infinite loops, there must always be at least one terminal state within the task definition; that is, a state from which no onward transitions are possible.

```json
curl -X POST 'https://<your-domain>.lusid.com/workflow/api/taskdefinitions' 
  -H 'Authorization: Bearer <your-api-access-token>'
  -H 'Content-Type: application/json-patch+json'
  -d '{
  "id": {
    "scope": "approvals",
    "code": "dataApproval"
  },
  "displayName": "Data Approval",
  "description": "A task definition for a simple data approval task",
  "states": [
    { "name": "Pending" },
    { "name": "Approved" },
    { "name": "Denied" },
    { "name": "NotRequired" }
  ],
  "fieldSchema": [
    {
      "name": "assignee",
      "type": "String"
    },
    {
      "name": "dataToApprove",
      "type": "String"
    },
    {
      "name": "reasonForDecision",
      "type": "String"
    }
  ],
  "initialState": {
    "name": "Pending",
    "requiredFields": [
      "dataToApprove"
    ],
  },
  "triggers": [
    {
      "name": "grant",
      "trigger": {
        "type": "External"
      }
    },
    {
      "name": "reject",
      "trigger": {
        "type": "External"
      }
    },
    {
      "name": "cancel",
      "trigger": {
        "type": "External"
      }
    }
  ],
  "transitions": [
    {
      "fromState": "Pending",
      "toState": "Approved",
      "trigger": "grant",
      "guard": "fields['assignee'] exists"
    },
    {
      "fromState": "Pending",
      "toState": "Denied",
      "trigger": "reject",
      "guard": "fields['reasonForDecision'] exists and fields['assignee'] exists"
    },
    {
      "fromState": "Pending",
      "toState": "NotRequired",
      "trigger": "cancel"
    }
  ]
}'
```

Part of a response is shown below. Note the `asAtVersionNumber` of the task definition, which increments if you [update the task definition](/v1/docs/how-do-i-create-a-task-definition#updating-a-task-definition):

```json
 {
  "id": {
    "scope": "approvals",
    "code": "dataApproval"
  },
  "version": {
    "asAtCreated": "2023-05-15T11:08:15.1522320+00:00",
    "userIdCreated": "00ujk6twb4jDcHGjN2p8",
    "asAtModified": "2023-05-15T11:08:15.1522320+00:00",
    "userIdModified": "00ujk6twb4jDcHGjN2p8",
    "asAtVersionNumber": 1
  },
  "displayName": "Data Approval",
  "description": "A task definition for a simple data approval task",
...
```

## Step 2: Creating a task to approve some data

Now that we have our task definition, we can test our workflow using a new approval. To do so, we create a [task](/v1/docs/how-do-i-create-a-task) from our task definition and include the required fields for the task to enter its initial state and set the workflow in motion.

To create a task we call the [CreateTask](https://www.lusid.com/docs/api/workflow/endpoints/tasks/CreateTask) API, passing in:

- The `scope` and `code` of the task definition.
- Optionally, any `correlationIds` to [track and group API calls](/v1/docs/tracking-and-grouping-api-calls-using-correlation-ids).
- The `name` and an appropriate `value` for the required `dataToApprove` field:

```json
curl -X POST "https://<your-domain>.lusid.com/workflow/api/tasks"
  -H "Authorization: Bearer <your-API-access-token>"
  -H "Content-Type: application/json"
  -d '{
  "taskDefinitionId": {
    "scope": "approvals",
    "code": "dataApproval"
  },
  "correlationIds": ["track-this-in-the-logs"],
  "fields": [
    {
      "name": "dataToApprove",
      "value": "Data x for y client"
    }
  ]
}'
```

Part of a response is shown below. Note the following:

- The `asAtVersionNumber` of the task, which increments if you update the task ([see step 3](/v1/docs/using-the-workflow-service-to-approve-new-or-updated-data#step-3-updating-the-task-to-grant-approval-manually)).
- The task is in its initial `state` of `Pending`.
- LUSID automatically creates a unique `id` for the task, which you can use to retrieve or update the task at any time.

```json
{
  "id": "79315001-b4bf-40e6-aae1-24850119aa9f",
  "correlationIds": [
    "track-this-in-the-logs"
  ],
  "taskDefinitionId": {
    "scope": "approvals",
    "code": "dataApproval"
  },
  "taskDefinitionVersion": {
    "asAtModified": "2023-05-15T11:08:15.1522320+00:00"
  },
  "version": {
    "asAtCreated": "2023-05-15T12:39:12.8015030+00:00",
    "userIdCreated": "00uji4twb4jDcHGjN2p7",
    "asAtModified": "2023-05-15T12:39:12.8015030+00:00",
    "userIdModified": "00uji4twb4jDcHGjN2p7",
    "asAtVersionNumber": 1
  },
  "state": "Pending",
  "terminalState": false,
  ...
```

## Step 3: Updating the task to grant approval manually

Our task now exists in the `Pending` state. To transition it to another state, such as `Approved`, we can call the [UpdateTask](https://www.lusid.com/docs/api/workflow/endpoints/tasks/UpdateTask) API, passing in the following:

- Within the request URL:
  - The unique `id` for the task we want to update.
  - A `trigger` to prompt a state transition. For our example, we'll use the `grant` trigger to move to the `Approved` state.
- The `name` and an appropriate `value` for the `assignee` field, which is required to meet the guard on the `grant` trigger.
- Names and appropriate values for any other fields we want to propagate, since this is a PUT operation and resources not specified are set to null (and therefore lost). In our example, we'll include the `dataToApprove` field in order to pass the data to the `Approved` state.

```json
curl -X POST "https://<your-domain>.lusid.com/workflow/api/tasks/79315001-b4bf-40e6-aae1-24850119aa9f?trigger=grant"
  -H "Authorization: Bearer <your-API-access-token>"
  -H "Content-Type: application/json"
  -d '{
  "fields": [
    {
      "name": "assignee",
      "value": "Joe Bloggs"
    },
    {
      "name": "dataToApprove",
      "value": "Data x for this y client"
    }
  ]
}'
```

Part of a response is shown below. Note the following:

- The `asAtVersionNumber` of the task has been incremented.
- The task is in its terminal `state` of `Approved`.

```json
{
  ...
  "taskDefinitionVersion": {
    "asAtModified": "2023-05-16T08:12:42.2904480+00:00"
  },
  "version": {
    "asAtCreated": "2023-05-16T13:57:37.9615180+00:00",
    "userIdCreated": "00uji4twb4jDcHGjN2p7",
    "asAtModified": "2023-05-16T13:58:08.3013150+00:00",
    "userIdModified": "00uji4twb4jDcHGjN2p7",
    "asAtVersionNumber": 2
  },
  "state": "Approved",
  "terminalState": true,
  ...
```

Since the task is now in its terminal state, it is not possible to trigger more state transitions for this particular task. To do anything else with this workflow, we need to create a new task from our task definition using the `CreateTask` API.

A series of one of more task definitions and workers that combine to complete a process.

Modelled as a Mealy state machine, a sequence of states and actions which encapsulate a workflow (or part of a complex workflow). Within a task definition, you can configure parameters for a task, such as states, fields and allowable state transitions. Task definitions also include actions, which can run workers to interrogate your data and generate child tasks. The task definition determines what a particular task does when an instance of that task occurs.

An occurrence of the workflow defined in a task definition. You can create multiple tasks from one task definition, typically with changing input values, for example you might create one task per LUID for securities to be screened.

The way in which data is passed into, around and out the other end of the task in the Workflow Service. Field names and data types are completely customisable and can be defined in a task definition. Field values can be specified when creating or updating a task.

A condition a task can exist in, for example **Pending**, **In progress**, or **Complete**. State names are customisable and can be defined in a task definition.

Movement from one state to another in the Workflow Service. Each state transition includes a start state, end state and is prompted by a trigger.

Defined within a state transition, a prompt which induces a state transition to occur in the Workflow Service.

An operational condition which must be met for a state transition to succeed in the Workflow Service. For example, all child tasks are in the state

**Complete**

.
