20 minlesson

Building an Entity-Trigger Workflow

Building an Entity-Trigger Workflow

In this lesson you will scaffold a real workflow that validates an Order before it is saved, add a configuration variable, a validation step, a logging activity, and a failure handler. You will then deploy it, trigger it, and read the logs.

By the end you will have a working Before-trigger workflow that blocks invalid Order saves and logs every execution.


Step 1: Scaffold the Workflow

Use the entity-trigger template. Place it in a feature folder for the orders domain:

bash
1npx cxtms create workflow order-status-validator \
2 --template entity-trigger \
3 --feature orders

The CLI creates:

features/orders/workflows/order-status-validator.yaml

Open the generated file and read through it before making any changes. Notice:

  • workflowId is already set to a UUID — do not change this.
  • filePath matches the file location — do not change this either.
  • There is a placeholder entity trigger, a sample activity, and event handlers.
  • executionMode is Sync — correct for a Before trigger.

Step 2: Configure the Workflow Metadata

Update the workflow section with a meaningful name, description, and log level:

yaml
1workflow:
2 workflowId: "<uuid from scaffold>" # Keep as generated
3 name: "Order Status Validator"
4 description: "Validates that an Order status transition is allowed before saving"
5 version: "1.0"
6 executionMode: Sync
7 logLevel: Information
8 isActive: true
9 enableAudit: true
10 filePath: "features/orders/workflows/order-status-validator.yaml"
11 tags: ["orders", "validation"]

Step 3: Configure the Before Trigger

Replace the scaffold's placeholder trigger with a concrete Before-Modified trigger on the Order entity. Add a condition so the workflow only fires when the status field is part of the change:

yaml
1triggers:
2 - type: Entity
3 entityName: "Order"
4 eventType: Modified
5 position: Before
6 name: OrderStatusChanged
7 conditions:
8 - expression: "[changes.status?] != null"

This condition prevents the workflow from running on saves that only change unrelated fields like notes or estimated delivery.


Step 4: Add a Configuration Variable

Your workflow needs to know which statuses are considered valid transition targets. Store this in app configuration so it can be changed without redeploying the workflow:

yaml
1variables:
2 - name: allowedStatuses
3 fromConfig: "apps.orderValidation.allowedStatuses"

In a real project, the configuration entry would be a JSON array: ["Pending", "Processing", "Dispatched", "Delivered", "Cancelled"]. The variable is loaded once at workflow startup.


Step 5: Build the Validation Activity

Replace the scaffold's placeholder activity. Name it Validate and add a Validation/Validate@1 step that checks the new status value:

yaml
1activities:
2 - name: Validate
3 steps:
4 - task: "Validation/Validate@1"
5 name: CheckStatusTransition
6 inputs:
7 rules:
8 - field: "status"
9 condition: "isNullOrEmpty([entity.status?]) = false"
10 message: "Status cannot be empty"
11 - field: "status"
12 condition: "[entity.status?] != 'Unknown'"
13 message: "Status 'Unknown' is not a valid transition target"

The Validation/Validate@1 task evaluates each rule and, if any rule fails, throws a validation error that stops the save. The error message is returned to the UI so the user sees a meaningful explanation.

Access the entity being saved via entity.fieldName — for Before triggers, entity reflects the proposed new values (after the user's edits, before the database write).


Step 6: Add a Logging Activity

Add a second activity to log a confirmation message after validation passes:

yaml
1 - name: Log
2 steps:
3 - task: "Utilities/Log@1"
4 name: LogValidationPassed
5 inputs:
6 message: "Order {{ entity.orderNumber }} status transition to '{{ entity.status }}' validated successfully"
7 level: Information

Because activities are sequential, Log only runs if Validate completes without error. If validation fails, the workflow stops at the Validate activity and the Log activity is never reached.


Step 7: Add an onWorkflowFailed Handler

Add a workflow-level failure handler so errors are recorded with context:

yaml
1events:
2 onWorkflowFailed:
3 - task: "Utilities/Log@1"
4 name: LogFailure
5 inputs:
6 message: "Order status validation failed for Order {{ entity.orderNumber }}: {{ exception.message }}"
7 level: Error

This handler fires whenever the workflow stops due to an unhandled error — including validation failures thrown by Validation/Validate@1. The exception.message variable contains the error detail.


Step 8: The Complete Workflow

Your finished YAML should look like this (with the actual UUID from the scaffold):

yaml
1workflow:
2 workflowId: "<uuid from scaffold>"
3 name: "Order Status Validator"
4 description: "Validates that an Order status transition is allowed before saving"
5 version: "1.0"
6 executionMode: Sync
7 logLevel: Information
8 isActive: true
9 enableAudit: true
10 filePath: "features/orders/workflows/order-status-validator.yaml"
11 tags: ["orders", "validation"]
12
13variables:
14 - name: allowedStatuses
15 fromConfig: "apps.orderValidation.allowedStatuses"
16
17triggers:
18 - type: Entity
19 entityName: "Order"
20 eventType: Modified
21 position: Before
22 name: OrderStatusChanged
23 conditions:
24 - expression: "[changes.status?] != null"
25
26activities:
27 - name: Validate
28 steps:
29 - task: "Validation/Validate@1"
30 name: CheckStatusTransition
31 inputs:
32 rules:
33 - field: "status"
34 condition: "isNullOrEmpty([entity.status?]) = false"
35 message: "Status cannot be empty"
36 - field: "status"
37 condition: "[entity.status?] != 'Unknown'"
38 message: "Status 'Unknown' is not a valid transition target"
39
40 - name: Log
41 steps:
42 - task: "Utilities/Log@1"
43 name: LogValidationPassed
44 inputs:
45 message: "Order {{ entity.orderNumber }} status transition to '{{ entity.status }}' validated successfully"
46 level: Information
47
48events:
49 onWorkflowFailed:
50 - task: "Utilities/Log@1"
51 name: LogFailure
52 inputs:
53 message: "Order status validation failed for Order {{ entity.orderNumber }}: {{ exception.message }}"
54 level: Error

Step 9: Validate the YAML

Always validate before deploying:

bash
1npx cxtms features/orders/workflows/order-status-validator.yaml

The validator checks against the JSON schema — it will flag missing required fields, incorrect task names, and type mismatches. Fix any reported errors before proceeding.


Step 10: Deploy to the Server

bash
1npx cxtms workflow deploy features/orders/workflows/order-status-validator.yaml --org 42

The CLI creates or updates the workflow on the CX server using the workflowId to identify it. The output confirms the deployment status.


Step 11: Test the Trigger

Open the CXTMS UI, find any Order, and try to save it with status set to Unknown. The save should be blocked with the validation message.

Then save with a valid status — the save should succeed and the Log step should record a confirmation message.

For a manual test without the UI, use the CLI with explicit inputs. Note that entity trigger workflows cannot be executed manually via the CLI in Before mode (there is no entity to provide) — use a Manual trigger workflow for CLI testing during development.


Step 12: Check the Logs

List recent executions:

bash
1npx cxtms workflow logs features/orders/workflows/order-status-validator.yaml --org 42

Download and view a specific execution log:

bash
1npx cxtms workflow log <executionId> --org 42 --console

The log shows each activity and step with timestamps, inputs, outputs, and any errors. The --json flag provides structured data including exact input and output values per step:

bash
1npx cxtms workflow log <executionId> --org 42 --json --console

Summary

In this lesson you:

  1. Scaffolded a workflow with npx cxtms create workflow --template entity-trigger
  2. Configured a Before trigger on Order Modified with a changes.status condition
  3. Added a fromConfig variable for app-managed configuration
  4. Built a Validation/Validate@1 step to block invalid status transitions
  5. Added a Log activity that runs only when validation passes
  6. Added an onWorkflowFailed handler to capture error context
  7. Validated the YAML with npx cxtms <file.yaml>
  8. Deployed with npx cxtms workflow deploy
  9. Verified execution through the UI and inspected logs with npx cxtms workflow logs and npx cxtms workflow log