Custom Workflows for Cityworks with FME

Sanae Mendoza
Sanae Mendoza
  • Updated

FME Version

  • FME 2023.0


An organization's asset management system can be complex and dynamic, with interconnected tables and databases. Managing the relationships between different data elements can be challenging. However, FME is excellent at handling such complex data scenarios due to its robust workflow-building options. With FME, organizations can automate data retrieval and synchronization from various Cityworks tables and records using API requests. 

By orchestrating these requests, FME ensures seamless and accurate data transfer between systems. This helps organizations maintain a well-coordinated and interconnected asset management system, streamlining operations, enhancing data integrity, decision-making, and resource optimization. Ultimately, FME empowers organizations to derive maximum value from their assets.

There are an infinite number of tasks and processes that you may want to automate in Cityworks with FME. These may take you beyond Safe Software’s pre-built custom transformers for Cityworks (e.g. WorkOrderCreator, InspectionCreator, etc.). Luckily, building your own requests for Cityworks, or any other application or workflow, is simple. 

This exercise expands on the previous by demonstrating how chaining requests can enhance workflows using Cityworks automation tools, such as webhooks. It provides step-by-step examples of creating custom API requests to interact with Cityworks. The outcome is a comprehensive automated solution that guarantees the completion of all necessary information at each stage of the process.




Step-by-Step Instructions

In the previous exercise, “Real-time Automations with Cityworks Webhooks”, we automated the creation of an inspection from a parent work order. However, we often want to continue a workflow to update related fields, systems, or records. 

To demonstrate how we can create start-to-finish workflows in Cityworks, we will build a custom request that queries a work order’s assets. From the results of the query, FME dynamically creates inspections. We complete the workflow chain by employing another custom request to migrate comments to our inspection. 

This example addresses a key common challenge for automating asset management workflows: related information is often distributed across multiple tables, therefore, updates cannot be performed in a single action. It is one of the reasons that FME performs especially well as an integration tool for Cityworks and similar systems. 

The exercise instructions assume you have completed the previous exercise: a workspace, FME Flow Automation, and Cityworks webhook. Use your existing workflow for this exercise, or download the workspace and create the webhook.

If you prefer to skip the previous exercise or do not have access to FME Flow, please download and complete the tutorial using the modified workspace.

Some instructions and images may differ when using the modified workspace. 


Part 1: Query Work Order Assets with a Custom API Request

In the section, we build an API request that queries a work order for its assets. With the query results, FME creates inspections for each asset.

Creating custom requests requires some knowledge of both the application you’re making requests to and APIs. Experience with using Cityworks as a web application and consulting the general Cityworks documentation may be helpful throughout the process. 

1. Prepare the Work Order
This exercise begins relies on an existing work order with assets. We can approach this one of two ways:

A. Manually create a brand new work order in Cityworks. Add the assets. Follow the instructions for Part 1, Step 9 of the previous exercise. This is the only option when using the modified workflow (see NOTE). 
B. Add these assets to an existing work order.

We’ll proceed with option B.

Navigate to Cityworks in a browser. Search for your work order.

Add three assets (e.g. “SWMANHOLE”) to the work order. 

To add an asset, go to the Assets section and select the Lookup Assets magnify glass icon.  In the Lookup Assets window under the Search tab, select Stormwater for the Asset Group. Select Feature and set the Asset Type as swManhole. If you do not know the Asset Uid, leave the Asset Uid parameter blank and click Verify Uids. The Asset Uid will load below. 

Take note of 3 Uids and Click Close. Reopen Lookup Assets and add the Assets with the selected Uids (e.g. 3, 4,7)

View the three assets in the Assets table.

Copy the work order id to a clipboard. Open the workspace from the previous exercise (or the standalone workspace) in FME Form (see NOTE). 

Edit the default value for the “webhook” parameter.

We will replace the default "webhook" parameter value with the following example webhook message:

{"WorkOrderId":"<work order id>" , "ApplyToEntity": “<work order entity>”, "Comments": "<work order comments>"} 

But first, we need to modify it so it points to an existing work order record in our Cityworks. Replace <work order id> with the value we just copied from Cityworks. Similarly, replace <work order entity> and <work order comments>

This is an example webhook message for the work order used in the previous exercise:

{"WorkOrderId":"147224", "ApplyToEntity": "SWMANHOLE", "Comments":"Testing%20my%20FME%20Flow%20Webhook%20workflow"}

Once the webhook parameter value is modified and entered, press OK to close the dialog. 

2. Review API Documentation: Inspection > CreateFromWorkOrder
Open the InspectionCreatorFromWO custom transformer. 

Most custom transformers only provide parameters for the bare minimum information required to create an inspection.

Additional Request Parameters provide a space to include more information with a request. You can include any parameters listed in REST API Documentation for that request*, like observations, employee information, materials, or (like in this example) assets. 

*All Cityworks custom transformers are built on an API request. To find out what request a custom transformer uses, refer to the documentation. You may also embed and edit the custom transformer to view the request in the HTTPCaller. 

To find out which parameters can be used with an InspectionCreatorFromWO custom transformer, navigate to your Cityworks REST API documentation in a browser (e.g. https://<your Cityworks URL>/cityworks/apidocs).

From the left-hand menu, under "Services", select AMS > Inspection


On the Inspection Service page, expand the CreateFromWorkOrder endpoint. 


Expand the Request Parameters section.

You might recognize some of the parameters marked "req", like "EntityType" and "InspTemplateId". These represent the base parameters in the InspectionCreatorFromWO custom transformer. 

The “Entity” parameter refers to assets that can be added to inspections. 


Unlike some of the other request parameters, the “Entity” parameter's data type column links to another Cityworks object: WorkOrderEntity. Click it to open. 

The WorkOrderEntity’s Data Types page opens. Data type pages describe each record in the Cityworks API. They are useful references for building Cityworks API requests or webhook messages. The WorkOrderEntity page tells us how assets are stored. 


To include an asset with our new inspection, we need to include base parameters “EntityUid” and “EntityType” in our request.

Recall, the webhook message ingested into the workspace contains 3 pieces of information about the work order: "WorkOrderId", "ApplyToEntity", and "Comments". It did not include an "EntityUid". 

So, why can’t we just modify the webhook message to include the "EntityUid"? 
Well, consider when we built the webhook message in the Cityworks Action Template (Part 2, Step 4). Our reference, the WorkOrderBase, didn’t list "EntityUid" as a possible parameter. Therefore, it couldn't be included in the webhook message. Many related records in Cityworks, like work orders and entities, are siloed across separate tables. This often presents a challenge to Cityworks admins attempting to automate workflows. However, we can use FME to connect these processes and relate records by chaining requests. 

To retrieve the "EntityUid" from our work order assets, we need to perform a query. It’s useful to know that while 1 work order can be related to many assets, 1 inspection can only be related to 1 asset. Therefore, making a query will answer two questions:

  • How many assets does our work order have? To determine how many inspections to create.
  • What are the assets’ EntityUids? To identify which assets need inspections. 

At this time, the FME Hub does not offer a custom transformer for querying work order assets. Therefore, we have an opportunity to build a custom API request for Cityworks. 

3. Assemble the Request Transformers 
First, we need to query the work order to fetch the asset information so that it can be passed downstream into the InspectionCreatorFromWO

In the FME workspace, delete the connection between the JSONFlattener and InspectionCreatorFromWo

Add the following transformers to the canvas: AttributeCreator, JSONValidator, and HTTPCaller
In that order, connect them to the JSONFlattener.

4. Build the Request Parameters
The process of building requests is typically a volley between REST API documentation and FME. Return to the Cityworks REST API documentation in a browser to find a request that queries work order assets. 
This time, navigate to AMS > WorkOrderEntity > ByWorkOrderIds. Expand the Request Parameters section. 

There is only 1 parameter flagged as “req”: "WorkOrderIds". 


Always take note of request parameter types (listed to the left). In this case: 

  • "GetGISData" = Boolean 
  • "WorkOrderIds" = List<string>

These data types determine how we JSON format the parameters. Formatting mistakes are by far the most common API workflow error. Therefore, we will use FME transformers to prepare and validate our JSON parameters before they're used in the API request. 

Back in the FME workspace, open the AttributeCreator

  • For the Output Attribute, type “CityworksParameters”. 
  • Next to Value, click the ellipses (“...”) to open the Text Editor. In the Text Editor, paste the following content: 
    "WorkOrderIds": ["@Value(WorkOrderId)"], 
    "GetGisData": true

A couple of things to note about the request parameters:

  • We formatted the request parameter values for their data types (Boolean and List<string>)
  • The "WorkOrderIds" parameter uses a workspace attribute value: "@Value(WorkOrderId)" 

OK to save. 

Open the JSONValidator. In the JSON Document, select the “CityworksParameters” attribute value. 

OK to save. 

5. Configure the HTTPCaller 
We’ve prepared the request parameters. Now, we must build the rest of the request.

Back in the Cityworks API Documentation, navigate to WorkOrderEntity > ByWorkOrderIds. Expand Show Request Query

Copy the request URL up until the “?” to your clipboard.


In the FME workspace, open the HTTPCaller

  • Paste the copied content into the Request URL parameter. 
  • Change the HTTP Method to Post. 

Next, tick the Use Authentication checkbox. 

  • Authentication Method: Web Connection.
  • Web Connection: <Your Cityworks Web Connection>

Expand the HTTPCaller’s Body section. 

  • Use the Upload Data drop-down menu to select “Multipart / Form Data”
  • Expand Multipart Upload.
  • Name: data
  • Type: “String Upload”
  • Value:  CityworksParameters

OK to close. 

6. Run the Request
Select Run to This… above the HTTPCaller to partially run the workspace. From the Visual Preview window, double-click the value under "_response_body". 

The HTTPCaller stores the response from Cityworks in the “_response_body” attribute. Double-click to view its entire contents. 

NOTE: For better readability, make sure to update the Syntax Highlighting to JSON and then select the Format JSON option. 

7. Parse the Request Response
To make the Cityworks JSON response more useful in our workspace, we can use more JSON transformers. 

JSON transformation plays a massive role in any web-based workflow. FME offers a number of JSON transformers that can parse, transform, or create JSON in your workspace. Different transformers return results in different ways. The JSONFragmenter excels at working with lists/arrays because it can create a new feature for each list element. In our case, we want to create a new feature for each returned asset. 

Add the JSONFragmenter to the canvas and connect it to the HTTPCaller. Open it up to configure its parameters: 

  • JSON Attribute: _response_body
  • JSON Query: json["Value"][*]
  • Flatten Query Results into Attributes: Yes
  • Recursively Flatten Results into Attributes: Yes
  • Attributes to Expose: EntityUid

OK to close. 

Select Run to This… above the JSONFragmenter. In the Visual Preview window, a successful query returns 3 features: one for each work order asset. The "EntityUid" values match the Cityworks asset ids we added to our work order in Step 1

Connect the JSONFragmenter to the InspectionCreatorFromWO

The workspace now looks something like this:

8. Configure Additional Request Parameters
In Step 1, we consulted the API documentation to understand how asset information is included with new inspections. 

  • We found the “Entity” parameter linked to the WorkOrderEntity
  • The WorkOrderEntity base parameters required “EntityType” and “EntityUid”. 

Now, we can employ this information to build the Additional Request Parameters in the InspectionCreatorFromWO

Open the InspectionCreatorFromWO

Use the Additional Request Parameters drop-down menu to select Text Editor. Paste the following text:

"Entity": {"EntityType": "@Value(ApplyToEntity)", "EntityUid": "@Value(EntityUid)"}

Select OK to return to the canvas.  

Rerun the workspace to produce the new features: a new inspection for each asset.

Navigate to Cityworks in the browser. 

From our work order, we can now view 3 “Custom Manhole” Inspections in Cityworks. Each inspection has an associated asset:

Save the workspace. 

Congratulations! You’ve now made a custom request that queries Cityworks. Some key takeaways:

  • Building the Request Body with AttributeCreators and JSONValidators reduce syntax errors 
  • The Cityworks REST API Documentation is the ultimate reference for building requests 
  • JSON transformers parse responses into features that can be processed downstream


Part 2: Update Inspection Fields with a Chained API Request 

Using chained requests is a common solution for “filling out” related records that are not part of the base parameters. This is especially applicable to asset management, permitting, or financial workflows, which tend to have more complex relationships. Other times, it’s just easier to break up a workflow into smaller, incremental requests. 

This exercise builds another custom API request that adds comment data to each inspection record. 

1. Build the Request Parameters
As always, our first step to building an API request is to review the Cityworks REST API documentation. The documentation allows us to determine if what we want to do is possible and what the requirements are. 

In this scenario, we want to migrate comments from the work order to the new inspections. 

In the Cityworks API Documentation, select AMS > Comment > Add endpoint. Expand the Request Parameters. Note the required parameters: “ActivitySid”, “ActivityType”, and “Comments”. 


In FME Workbench, delete the connection between the InspectionCreatorFromWO transformer and WorkOrderInspections writer. 

Add the following transformers to the canvas: AttributeCreator, JSONValidator, and HTTPCaller. In that order, connect them to the InspectionCreatorFromWO.

Open the AttributeCreator_2. For the Output Attribute, enter “CityworksParameters2”. 

Next to Value, click the ellipses (“...”) to open the Text Editor.  In the Text Editor, paste the following content:  

"ActivityType": 2, 
"ActivitySid": @Value(_inspection_id), 
"Comments": "@Value(Comments)"


Note: Please review your documentation for CommentActivityType Id at https://<your Cityworks Instance>/apidocs/#/enum-info;enumType=CommentActivityType

OK to save. 

Open the JSONValidator_2. In the JSON Document, select the “CityworksParameters” attribute value. 

2. Configure the HTTPCaller
In the Cityworks REST API Documentation, navigate to AMS > Comment > Add. Expand Show Request Query

Copy the URL up until the “?” to your clipboard.


In the FME workspace, open the HTTPCaller_2. Paste the copied content into the Request URL parameter. 
Change the HTTP Method to "Post". 

Tick the Use Authentication checkbox. 

  • Authentication Method: Web Connection
  • Web Connection: <Your Cityworks Web Connection>

Expand the HTTPCaller Body section. 

  • Use the Upload Data drop-down menu to select “Multipart / Form Data”
  • Expand Multipart Upload.
    • Name: data
    • Type: “String Upload”
    • Value:  CityworksParameters2


OK to close the HTTPCaller_2. 

On the HTTPCaller_2, select Run to This… Ensure a “_response_body” is returned. 

3. Parse the JSON 
Add a JSONFragmentor to the canvas and connect it to the HTTPCaller_2. Open it up to configure its parameters. 

  • JSON Attribute: _response_body
  • JSON Query: json["Value"]
  • Flatten Query Results into Attributes: Yes
  • Recursively Flatten Results into Attributes: Yes
  • Attributes to Expose: Comments AuthorName CommentId

Press OK to save and close. 

Select Run to This… to ensure 3 features with unique “CommentId” are returned. 

We’ve successfully added comments to each of our inspections! 

Refresh one of the inspections in Cityworks to view a newly added comment. 


Save the workspace. 

6. Use the Workspace in an Automation
We can update the FME Flow Automation (CityworksWOReceiver) built in the previous exercise to automate this workflow.

In FME Workbench, open the Publishing Wizard. Choose the same “Cityworks” repository to overwrite the existing InspectionFromWO.fmw.


In FME Flow, ensure that the CityworksWOReceiver Automation is running. 


To test the automation, we need to trigger the webhook workflow by updating our work order’s status. In Cityworks, search for our existing work order. 

Change its Status to “5-Completed”. 

Save the work order. 

Navigate back to our FME Flow CityworksWOReceiver Automation. Review the Log. This time, there will be 3 logged inspection ids for the 1 webhook message. 

Reviewing each inspection in Cityworks reveals an associated asset and comments. 

Congratulations! You’ve now created a workflow that features three distinct requests to Cityworks: 

  • Query a work order for asset information
  • Creates new inspections from each asset
  • Adds comments to each inspection

This workflow is a simplified example of how chained, custom requests in FME can automate front-to-end Cityworks workflows. 

Next steps? Enrich this workflow by using a different webhook event trigger, connecting other data sources, performing data transformations, or making additional requests.  



The first step to troubleshooting Cityworks REST API workflows in FME requires identifying the source of the problem. The following describe common scenarios and offer some possible solutions.

If the HTTPCaller produces a "Rejected" result, the API request has failed. This is likely caused by a malformed API request configuration. 

This request failed because the HTTPCaller was missing a web connection. The HTTP status code returned is 400 (unauthorized).

Use Visual Preview to inspect the "Rejected" data. The status code ("_http_status_code") and error message ("_error") may tell you more about the issue. Compare the status code to an HTTP status code reference. 

Issues may be found in the HTTPCaller Request URL or Authentication parameter configuration.  

If the HTTPCaller produces a successful result, but the "_response_body" attribute contains HTML. This may mean the request triggered an unhandled exception in Cityworks.
  • An unhandled error may be caused by incorrect JSON formatting. Carefully inspect the request Body.
    • Is the JSON missing any colons, quotes, or curly braces?
  • Issues may be found in the HTTPCaller Request URL or Authentication parameter configuration.


If the HTTPCaller produces a successful result, but the "_response_body" does not contain the expected values. This may mean the request has triggered a known error in Cityworks. 


_http_status_code is 200, but the Cityworks response contains an error status code. Status 3 indicates a problem with authentication credentials (check your web connection!) 


This Cityworks response contains a warning message that tells us the request was missing a required parameter.


Cityworks returns its own error messages. Inspect the response for status codes and error/warning messages. Reference the Cityworks REST API Documentation Introduction page for return messages. Not all errors are descriptive and, thus, require more investigative work. 


Errors may be produced when attempting to use values that are not valid for your specific Cityworks configuration.

  • Are all parameters flagged as required ("req") included and are all parameter values valid? Consult with your organization's Cityworks administrator if unsure. 
e.g. the "WorkOrderId" does not exist, the template/entity/asset id is invalid

Many types of errors are caused by incorrect JSON formatting. Carefully inspect the request Body.

  • Is the JSON missing any colons, quotes, or curly braces?
  • Are the parameter data types formatted correctly? Double-check the parameter data types in the Cityworks REST API Documentation.
e.g. a string parameter has quotes, an integer does not have quotes, arrays are comma-delimited lists wrapped in square braces, etc.
e.g. a "WorkOrderId" may be a string in one request, but an integer in another.

Additional Resources

Was this article helpful?



Please sign in to leave a comment.