Extensions add a specific feature to ITM Platform. They can be used to extend functionality or as a connector between ITM Platform and a third-party system.
You can create your own custom extensions. This guide will help you get started and serve you as a reference when you become an expert.
Each section of this guide is organized into three blocks: Learn by example, Guide, and Reference.
The terminology section will help you with the most commonly used terms.
- You can access the extension panel in ITM Platform on the CONFIGURATION CUSTOMIZATION menu.
- You need a Full Access license to operate on existing global extensions and edit or create existing ones.
- You can create and edit your own extensions in the "code editor" section, which will give you access to the extension script. Notice that you can't edit global extensions.
- A extension script is a JSON file that follows a structure of instructions that will perform the functionality you need.
- A extension can be triggered based on time (scheduler) or specific ITM Platform events. For example, every day or when a purchase is updated.
- Once triggered, the extension will initiate actions that can be chained to one another, forming a sequence of actions called features.
- Once a extension is ready, you can activate it.
A extension script is defined in a single JSON file, following this structure:
Learn by example
{
"name": "unique-identifier",
"svgString": "<svg></svg>",
"details": {},
"features": [{
"trigger": "scheduler",
"actions": [{}, {}]
}
],
"config": [{}, {}],
"mapping": []
}
All these examples include the code and a tutorial. It is recommended to start with the first and go into more complex examples as you understand the simpler ones.
- Example 1. When a project updates, send a message to a static email address.
- Event-based
- Minimal configuration: activation only
- Example 2. When a purchase's actual value updates, send an email to a static address if the amount exceeds the purchase budget.
- Event-based
- Using conditions
- Example 3. Send a daily email to the project manager of a specific project containing all tasks whose end date is later than today.
- Scheduler-based
- Loop and conditions
- Example 4. Synchronize ITM Platform's clients with Hubspot's companies: only those having the Hubspot's property
sync_itm_platform
set totrue
.- Scheduler-based
- Connector to a third-party system
- Example 5. Allow users to input revenue's Actual Amount if <= Projected Amount.
- Event-based, synchronous
You can also look at the code of the public extensions such as the Help Scout connector or the Zendesk connector which include both source code and comprehensive developer guides.
Learn by example
{
"name": "unique-name",
"svgString": "<svg xmlns='http://www.w3.org/2000/svg' fill='#495d73' viewBox='0 0 640 512'><path d='M640 256c0 35.35-21.49 64-48 64c-32.43 0-31.72-32-55.64-32C522.9 288 512 298.9 512 312.4V416c0 17.67-14.33 32-32 32h-103.6C362.9 448 352 437.1 352 423.6C352 399.1 384 400.4 384 368c0-26.51-28.65-48-64-48s-64 21.49-64 48c0 32.43 32 31.72 32 55.64C288 437.1 277.1 448 263.6 448H160c-17.67 0-32-14.33-32-32V312.4C128 298.9 117.1 288 103.6 288C79.95 288 80.4 320 48 320c-26.51 0-47.1-28.65-47.1-64S21.49 191.1 48 191.1c32.43 0 31.72 32 55.64 32C117.1 223.1 128 213.1 128 199.6V95.1C128 78.33 142.3 63.1 160 63.1l103.6 0C277.1 63.1 288 74.9 288 88.36C288 112 256 111.6 256 143.1C256 170.5 284.7 192 320 192s64-21.49 64-48c0-32.43-32-31.72-32-55.64c0-13.45 10.91-24.36 24.36-24.36L480 63.1c17.67 0 32 14.33 32 32v103.6c0 13.45 10.91 24.36 24.36 24.36c23.69 0 23.24-32 55.64-32C618.5 191.1 640 220.7 640 256z'/></svg>",
"details": {
"title": {"en": "My Extension","es": "Mi extensión","pt": "Meu extençao"},
"version": "0.8",
"shortDescription": {"en": "desc","es": "desc","pt": "desc"},
"longDescription": {"en": "long desc","es": "long desc","pt": "long desc"},
"updated": "2022-05-25",
"developer": "developer-name"
}
}
Guide
The general definition will provide basic information about the extension you are building. The title, the short description, and the icon will appear on the extension button within the extension panel.
Reference
-
name
: It will be used as a identifier for the extension and must be unique. It can't have spaces or special characters. -
svgString
: Inline SVG that will be used as the extension icon. -
details
title
: Main identifier of the extension for the end-user. It accepts multi-languageversion
: Version number. Take into account that the system will not store previous versions.shortDescription
It will appear in the extension panel. It accepts multi-languagelongDescription
: It accepts multi-languageupdated
: yyyy-mmm-dd date formatdeveloper
: Who developed the extension
- Extension
name
must exist and be unique
Learn by example
{"features": [
{
"trigger": "event",
"extension": "providers-demo",
"entity": "Project",
"description": "Retrieving providers",
"event": "updated",
"actions": []
}
]}
Guide
The Features section defines the behavior of the extension. It determines what will trigger it, and the actions that will follow.
When a feature is triggered by an event, such as a task update, you must specify the entity
(task, in our example) and the event
(update, in the same example).
When the feature is triggered by the scheduler, you must create the synchronization frequency configuration option, so the user can set the value.
Reference
(*) Denotes a mandatory field
-
trigger
: It can beevent
,scheduler
, orwebhook
* -
entity
: Entities * mandatory for theevent
trigger -
event
System Events * mandatory for theevent
trigger -
async
:true
orfalse
It applies to theevent
trigger and determines whether the execution in ITM Platform that triggered it will stop (false
) or the extension will run in a parallel thread (true
). The default value isasync
:true
if not specified. -
description
: Useful for extension developers. It will not be displayed to the end-user. -
actions
: An array of actions.⚠️ If you make the feature synchronous (async: false
), the execution in ITM Platform will pause until the extension responds, resulting in a potential lousy user experience. Make features synchronous only when necessary, and keep the logic as simple as possible. For example, when validating user inputs.
- Each extension should contain at least one feature.
- Triggers must be either
scheduler
orevent
- Each feature will have actions array.
Learn by example
{
"action": "restcall",
"url": "https://api.itmplatform.com/v2/mycompany/projects/{{ input.project.Id }}",
"method": "GET",
"token": "{{ logininfo.Token }}",
"description": "Retrieve project",
"output": "project",
"payload": "",
"headers": "{\"token\" : \"{{ logininfo.Token }}\", \"myheader\" : \"header value\"}"
"dataType": "application/json"
}
Guide
Actions are pieces of code executed when a feature is triggered. Actions are typically chained to one another by sending the output
value from the parent action to the child action.
Actions admit loops and conditionals.
Reference
action
: specifies the action type from the different action types.token
: Used to call the ITM Platform APIdescription
: For developersoutput
: output object used in further actins as theinput
objectpayload
: body of the request. It supports templates, used to replace text with variable content.headers
: Optional. Valid forrestcall
only. You can pass additional required headers to the rest calltoken
: Optional. Valid to call ITM Platform's api. Shortcut to adding it as a header, like so:"headers": "{\"token\" : \"{{ logininfo.Token }}\"}"
dataType
: Optional. It will parse the result as JSON or XML. Possible values areapplication/json
andapplication/xml
. When omitted, the result will be a string.
When saving an extension, the following validations will run. If the script doesn't pass them, you will be able to save it but not activate the extension.
- The action value can be one of:
restcall
,soapcall
,validate
,map
,email
, orloop
- If the action value is
restcall
, it can containurl
,method
(defaults toGET
) andpayload
(not mandatory). - If the action value is
restcall
, it can have acondition
. - If the action value is
soapcall
, it must include asoapaction
,url
andpayload
. - If the action value is
restcall
orsoapcall
, there is an authentication attribute and its value is basic, it must have username and password. - If the action value is
validate
, it must havevalidationCondition
andmessage
. - If the action value is
map
, it must havetemplate
. - If the action value is
loop
, it must havevar
,condition
,actions
andoutput
.
The action
value can be one of: restcall
, soapcall
, validate
, map
, email
, or loop
Performs a REST call to any third-party API or ITM Platform's.
Learn by example
{
"action": "restcall",
"url": "https://jsonplaceholder.typicode.com/todos/",
"method": "GET",
"description": "Retrieves a to-do list",
"output": "todos",
"payload": "",
"dataType": "application/json"
}
Guide
A REST call is the most common way to get or post data in any API, including ITM Platform's. Use it as you would with any tool, such as Postman, curl
, or your favorite programming language.
Reference
action
: Must berestcall
url
: See the url section.method
: specifies the HTTP method (GET
,POST
,PUT
,PATCH
,DELETE
)description
: Reference for the developeroutput
: output object used in further actionspayload
: body of the request.dataType
: is optional and it will parse the result as json so it can be send to any next step, if not passed then the result will be a string
Performs a SOAP call to any third-party system.
Learn by example
{
"action": "soapcall",
"url": "http://testurl.com/testservice",
"soapaction": "testmethod",
"payload": "<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:tem=\"http://tempuri.org/\"><soapenv:Header/><soapenv:Body><tem:CallPost><tem:name>{{ input.SomeInt }}</tem:name></tem:CallPost></soapenv:Body></soapenv:Envelope>",
"dataType": "application/json",
"authentication": "basic",
"username": "testuser",
"password": "testpass",
"output": "jsonoutput"
}
Guide
A SOAP action will require a url
, a soapaction
, a payload
and an output
. The dataType
attribute must be "application/json"
. It can also include an authentication attribute, along with user
and pass
.
Data Types
The following example parses the output
as xml, which can be used later on to retrieve values with mapping
{
"action": "soapcall",
"url": "http://testurl.com/testservice",
"soapaction": "testmethod",
"payload": "{{ output1 }}",
"dataType": "application/xml",
"output": "dataoutput"
}
{
"action": "soapcall",
"url": "http://testurl.com/testservice",
"soapaction": "testmethod",
"payload": "{{ output1 }}",
"dataType": "application/json",
"output": "dataoutput"
}
Reference
action
: Must besoapcall
soapaction
: SOAP actionurl
: SOAP URLpayload
: The payload of the SOAP Action needs to be a SOAP Envelop message like specified here https://www.tutorialspoint.com/soap/soap_message_structure.htm The easiest way to create an envelop that you can use later, is to use a tool like SOAPUI, and then replace the values.authentication
: The type of authentication required, if any. The accepted values are.basic
which will requireuser
andpass
token
which will requiretoken
user
: User name when basic authentication is required.pass
: Password when basic authentication is required.token
: Bearer token when token authentication is required
Learn by example
{
"action": "map",
"template": "{ \"name\": \"{{ input.name }}\", \"value\": \"{{ input.value }}\" }",
"dataType": "application/json",
"output": "outputVar"
}
Guide
The map
actions allow mapping values from existing variables. In the example above, the variables name
and value
coming from input
will be transformed to JSON.
Reference
template
: See template syntaxoutput
: output object used in other actions.dataType
: is optional, and it will parse the result as JSON so it can be sent to any next step. when omitted the result will be a string
Used in synchronous
features, it performs a validation before proceeding.
Learn by example
{
"action": "validate",
"validateCondition": "Convert.ToInt32(input.Status.Id) == 10 && Convert.ToString(input.Description) == \"\"",
"message": "When using the status {{ input.Status.Description }} you need to provide a description"
}
Guide
The validate
action will require a validationCondition
, and a message
that will be displayed to the final user if the condition is not met. It is generally used to perform custom validations that may need to change the way ITM platform works. For example, if you need to validate that a particular field has a value depending on other fields.
Reference
action
:validate
validateCondition
: It follows the same rules and syntax of action conditionalsmessage
: String displayed to the final user in the ITM Platform's interface in case the condition is not met.
Sends an email to the given address/es
Learn by example
{
"action": "email",
"to": "yourname@yourcompany.com",
"subject": "This subject admits templates",
"body": "<html><h1>This email body admits templates too!</h1></html>"
}
Guide
Sends an email to the specified recipient(s). The sender will be notifier@itmplatorm.com, an address that doesn't admit replies.
Reference
action
: Must beemail
to
: Mandatory, indicates a comma-separated list of email addresses.subject
: Contains the subject text. It allows templates.body
: contains the message body. It allows templates.
The loop
action will iterate over elements coming from previous calls.
There are different types of loops: simple, with variables, an with variables and conditions.
Learn by example
{
"trigger": "event",
"event": "updated",
"entity": "Project",
"actions": [
{
"action": "loop",
"loop": {
"var": "myarray",
"output": "item",
"index": "index"
},
"actions": [
{
"action": "restcall",
"url": "http://your-company/{{ item.name }}/{{ index }}",
"method": "post",
}
]
}
]
}
Guide
A loop action will parse an object coming from a previous action and run an actions array over the outcome.
Reference
We can also loop with variables inside an action. For example:
Learn by example
{
"trigger": "event",
"event": "updated",
"entity": "Project",
"action": "loop",
"loop": {
"var": "myarray.values",
"output": "singleElement",
},
"actions": [
{
"action": "restcall",
"url": "http://testurl.com/testapi",
"method": "POST",
"payload": "{\"Columns\": \"Id,Name,JiraId,Type.Id,MethodTypeId\",\"filter\": { \"JiraId\" : {{ singleElement.id }} } }",
"output": "outputVar",
"dataType": "application/json"
},
Guide
Reference
We can also loop with variables and conditions inside an action. For an example:
{
"trigger": "event",
"event": "updated",
"entity": "Project",
"action": "loop",
"loop": {
"var": "myarray.values",
"output": "singleElement",
"condition": "somevariable != null"
},
"actions": [
{
"action": "restcall",
"url": "http://testurl.com/testapi",
"method": "POST",
"payload": "{\"Columns\": \"Id,Name,JiraId,Type.Id,MethodTypeId\",\"filter\": { \"JiraId\" : {{ singleElement.id }} } }",
"output": "outputVar",
"dataType": "application/json"
},
{
"action": "restcall",
"url": "http://testurl.com/testapi",
"condition": "Convert.ToInt32(outputVar.total) > 0",
"method": "PATCH",
"payload": "{\"Name\": \"{{ singleElement.name }}\",\"Description\": \"{{ singleElement.description }}\" }",
"output": "updatedoutputVar",
"dataType": "application/json"
}
]
}
Guide
Reference
Conditionals
Learn by example
{
"action": "restcall",
"url": "https://api.hubapi.com/companies/v2/companies/recent/modified?hapikey={{config.apikeyHubspot }}&since={{ context.LastExecution }}",
"condition": "{{ context.LastExecution }} != null",
"method": "GET",
"description": "retrieve all clients filter lastExecutiondate",
"output": "companiesHS",
"payload": "",
"dataType": "application/json"
}
Guide You can put a condition to the event to check whether the actions should be executed.
{
"condition": "{{input.SomeObject.Array.0.Array.1.SomeData}} == 3"
}
The webhook
trigger enables ITM Platform extension features to be initiated by HTTP requests sent from external systems. This trigger is particularly beneficial for real-time integrations where a third-party application, such as a ticketing system like Zendesk, needs to instantly inform ITM Platform about events happening within that system. For instance, the Zendesk Connector uses a webhook
trigger to create or update a task in ITM Platform based on an update event received from Zendesk.
When a feature with a webhook
trigger is executed, the data included in the external request's payload becomes available to the extension's subsequent actions. This data is typically accessed via the input
object within the action definitions.
Reference When defining a feature that is triggered by a webhook, include the following properties:
- trigger: Must be
"webhook"
. This field is mandatory. - AccountId: Typically defined using the variable
@@AccountId@@
. This is automatically populated based on the company URL used in the incoming webhook request. - description: An optional field for developers to describe the feature; it is not shown to the end-user.
- condition: An optional condition that must evaluate to true for the actions array to be executed. This follows the same rules and syntax as action conditionals.
- event: (Optional) While the ITM Platform trigger type is
webhook
, the external system's payload often describes the specific event that occurred (e.g., an "updated" event in Zendesk). This external event information is typically available within theinput
object for use in conditions or actions. - async: Specifies whether the extension execution runs in a parallel thread (
true
) or pauses the system process that initiated it (false
). The default value istrue
. For webhook triggers,async: true
is commonly used. - actions: A mandatory array defining the sequence of actions to be performed when the webhook is triggered and any conditions are met.
Calling the Webhook:
To trigger an extension feature configured with trigger: "webhook"
, the external system must send an HTTP request to a dedicated endpoint within ITM Platform. The standard URL structure for this endpoint is:
https://api.itmplatform.com/v2/{companyURL}/webhooks/{extension-name}
{companyURL}
: This part of the URL represents the URL-friendly name of your ITM Platform account (e.g.,globalcorp360
). This corresponds to the@@AccountName@@
script variable available within the extension environment.{extension-name}
: This is the unique internal identifier for your extension script, defined in thename
property at the beginning of the JSON file. This name should not contain spaces or special characters.
Example URL:
Based on the Zendesk Connector, which has the internal name 76ee1244-77ea-454d-a5f6-c41bc98dd6a2
, and assuming a hypothetical ITM Platform company URL name of globalcorp360
, the specific webhook URL to trigger this extension would be:
https://api.itmplatform.com/v2/globalcorp360/webhooks/76ee1244-77ea-454d-a5f6-c41bc98dd6a2
Tip – posting the full webhook body
If you need to send the whole input
object as JSON in a restcall
(or any action that has a payload
), use the built‑in Handlebars helper json
, which converts any object to a JSON‑formatted string:
json
serializes the object for you .- Wrap it in triple braces
{{{ … }}}
so Handlebars doesn’t escape the quotes. - Do not add extra quotes around the expression—
json
already returns a valid JSON string, ready to be used as the request body.
With this one‑liner the complete webhook payload is forwarded exactly as ITM Platform received it, making debugging much easier.
The "config"
array will determine the content rendered in the extension's configuration tab on ITM Platform, displaying all fields required for your extension to operate.
Learn by example
{
"config": [
{
"name": "option-name",
"label": {
"en": "Label in English",
"es": "Etiqueta en español",
"pt": "RĂłtulo em inglĂŞs"
},
"tooltip": {
"en": "A brief description for the tooltip",
"es": "Una breve descripciĂłn para el tooltip",
"pt": "Uma breve descrição para o tooltip"
},
"type": "date",
"required": true
}
]
}
}
Guide
These fields are represented by the configuration options within the config
array.
Once the user fills out the ITM Platform's "Configuration" tab, the values will be available through the config
object, available to be used in features and actions.
These are some examples of the usage of the config
object throughout a script:
"url": "{{ config.url }}/rest/api/3/project/search?maxResults=50&startAt=0"
"username": "{{ config.user }}"
"payload": "{\"Name\": \"{{ singlejiraproject.name }}\",\"Description\": \"{{ singlejiraproject.description }}\",\"InternalCode\": \"{{ singlejiraproject.key }}\",\"JiraURL\": \"{{ config.url }}\",\"SynchronizationSource\": \"Jira\"{{#ifempty singlejiraproject.projectCategory }}{{else}},\"TypeId\": \"{{ map config.mapping.projecttype.external singlejiraproject.projectCategory.id }}\"{{/ifempty}} }"
These configuration options can define any custom value that you need to store (eg. the API key required to log in), but it also interprets a few hardcoded values.
The identifier for each configuration option is the name
property
Reference
name
: Custom configuration options accept any any valid unique name. Hardcoded configuration options are"isactive"
,"synchronizationfrequency"
,"syncafterdate"
label
: Message that will show before the user input. For example"Start Date"
tooltip
: Message that will show when the user hovers over the information icon.type
: Defines the type of the user input. The valid values arestring
,password
,checkbox
,date
,dropdown
, andheader
.required
: specifies if this configuration is required in order to execute the extension. It only applies to the typesstring
,password
,date
, anddropdown
.
The header
type will render regular section header Section headers on ITM Platform's configuration tab, adding a adding a visual separation between fields.
{
"config": [
{
"name": "header-credentials",
"label": {
"en": "Credentials section",
"es": "SecciĂłn de credenciales",
"pt": "Seção de credenciais"
},
"type": "header"
}
]
}
The hardcoded configuration options will be identified by their name and define a specific behavior in the extension interpreter. These hardcoded options are:
"name": "isactive"
"name": "synchronizationfrequency"
Provides the option for the user to activate the extension within the "Configuration" tab.
Learn by example
{
"config": [
{
"name": "isactive",
"label": {
"en": "Activate connector",
"es": "Activar conector",
"pt": "Ativar o conector"
},
"tooltip": {
"en": "When selected, the connector will be active and will start synchronizing data",
"es": "Cuando esté seleccionado, el conector estará activo y comenzará a sincronizar datos",
"pt": "Quando selecionado, o conector estará ativo e começará a sincronizar dados"
},
"type": "checkbox",
}
]
}
Guide This will display a checkbox to activate the extension on the configuration tab in ITM Platform.
Reference
Properties with special considerations:
name
: Must be"isactive"
type
: Must be"checkbox"
Provides the configuration option for the user to set the frequency at which the extension will be triggered. It is required when the feature is triggered by scheduler
Learn by example
{
"config": [
{
"name": "synchronizationfrequency",
"label": {
"en": "Synchronization frequency (minutes)"
},
"tooltip": {
"en": "Frequency you wish to establish for the synchronization of the data from your account"
},
"type": "string",
"required": true
}
]
}
Guide It will display in the "Configuration" tab a user input to introduce the synchronization frequency in minutes for the scheduler.
The minimum frequency is 60 minutes. We recommend setting the frequency at the maximum your business case accepts.
It will apply to the feature(s) that have the scheduler
trigger.
Reference
Properties with special considerations:
name
: Must be"synchronizationfrequency"
type
: Must be"string"
The mapping configuration allows you to establish an equivalence between two systems, typically ITM Platform and a third party.
This is useful to create synchronizations based on the values of one or more fields.
As an extension developer, you don't set those relationships. You give the user the means to create relationships in the configuration tab in ITM Platform.
Learn by example
{
"mapping": [
{
"name": "projecttype",
"label": {
"en": "Project Type",
"es": "Tipo de proyecto",
"pt": "Tipo de Projeto"
},
"external": {
"method": "GET",
"url": "@@url@@/rest/api/3/project/type",
"authentication": "Basic",
"username": "@@user@@",
"password": "@@pass@@",
"id": "key",
"name": "formattedKey",
"columnName": {
"en": "Jira",
"es": "Jira",
"pt": "Jira"
}
},
"internal": {
"method": "GET",
"url": "v2/@@companyId@@/GetProjectTypes",
"id": "Id",
"name": "Name",
"columnName": {
"en": "ITM Platform",
"es": "ITM Platform",
"pt": "ITM Platform"
}
}
}
]
}
Guide
In the example above, the configuration panel will show two columns filled with the project types of ITM Platform and another with the project types of Jira. This will allow the user to establish that --for example- the project type "Software" in ITM Platform is equivalent to the project "Development" in Jira.
Reference
external
: The system A.internal
: The system B.url
: Endpoint that will gather the values.authentication
: The type of authentication required, if any. The accepted values are.basic
which will requireuser
andpass
token
which will requiretoken
user
: User name when basic authentication is required.pass
: Password when basic authentication is required.token
: Bearer token when token authentication is requiredid
: ID or name of the property containing the value of interest.name
: Name displayedcolumnName
: Label for the column that will appear in the config panel
The following are the events that ITM Platform triggers.
Trigger | Entity | Action | When | Input |
---|---|---|---|---|
scheduler | This executes from scheduler | context.LastExecution | ||
event | Task | inserted | When a task is inserted | { "accountId": "accountId", "projectId": "projectId", "userId": "userId", "task": { "Id": "taskId", "Name": "taskName", "JiraTaskId": "JiraTaskId", "KindId": "taskKindId" } } |
event | Task | updated | When a task is updated | { "accountId": "accountId", "projectId": "projectId", "userId": "userId", "task": { "Id": "taskId", "Name": "taskName", "JiraTaskId": "JiraTaskId", "KindId": "taskKindId" }} |
event | Project | inserted | When a project is created | { "accountId": "accountId", "userId": "userId", "project": { "Id": "projectId", "Name": "projectName", "TypeId": "typeId", "Description": "description" }} |
event | Project | updated | When a project is updated | { "accountId": "accountId", "userId": "userId", "project": { "Id": "projectId", "Name": "projectName", "TypeId": "typeId", "Description": "description" }} |
event | Purchase | inserted | When a Purchase is created | { "accountId": "accountId", "projectId": "projectId", "userId": "userId", "purchase": { "Id": purchaseId, "Name": "purchaseName", "ActualAmount": "actualAmount", "ProjectedAmount": "projectedAmount", "StatusId": "statusId" } } |
event | Purchase | updated | When a Purchase is updated | { "accountId": "accountId", "projectId": "projectId", "userId": "userId", "purchase": { "Id": purchaseId, "Name": "purchaseName", "ActualAmount": "actualAmount", "ProjectedAmount": projectedAmount", "StatusId": "statusId" } } |
event | Revenue | pre insert | When a Revenue is going to be created | { "ProjectedAmount": "projectedAmount", "ActualAmount": "actualAmount", "ProjectId": "projectId" } |
event | Revenue | inserted | When a Revenue is created | { "Id": "revenueId", "Name": "revenueName", "DueDate": "dueDate", "ProjectedAmount": "projectedAmount", "ActualAmount": "actualAmount", "Status": "statusId", "ProjectId": "projectId" , "UserId": "UserId", "AccountId": "AccountId" } |
event | Revenue | pre update | When a Revenue is going to be updated | { "ProjectedAmount": "projectedAmount", "ActualAmount": "actualAmount", "ProjectId": "projectId" } |
event | Revenue | updated | When a Revenue is updated | { "Id": "revenueId", "Name": "revenueName", "DueDate": "dueDate", "ProjectedAmount": "projectedAmount", "ActualAmount": "actualAmount", "Status": "statusId", "ProjectId": "projectId" , "UserId": "UserId", "AccountId": "AccountId" } |
Events don't bubble up how you may be used in other environments. But if an entity event affects others, such as its parents, these parents will also trigger an event.
For example, you change the end date of a task. That will trigger the event updated
on the entity Task
. Now, consider these two scenarios:
- The task end date doesn't change the project's end date (or any other value, such as the cost). Then the project will not trigger any event
- The task end date pushes forward the project's end date. Then that will trigger the event
updated
on the entityProject
Wherever templates are supported, you can use curly braces to wrap an expression {{ expression }}
to generate the text that you need.
The template system is based on Handlebars and supports all the native expressions, plus a set of additional expressions that will come in handy when building an extension.
caveat: If you need to include quotes within the template (tipically in the
payload
), you need to escape internal quotes with a backlash.{{ "payload": "{\"Value\": 23 }}
The following are Handlebars expressions created to specifically interact with the extension interpreter.
These helpers extend the template language with data type conversions, value replacements, and conditionals created to specifically interact with the extension interpreter.
Helper | Purpose | Example |
---|---|---|
Data type converters | ||
date |
Converts a string to a date type. | {{ date config.syncafterdate 'yyyy/MM/dd HH:mm' }} Example: "payload": "{\"jql\" : \"updated >= '{{ date config.syncafterdate 'yyyy/MM/dd HH:mm' }}'\" }" |
timespan |
Converts a string to a time span. | {{ timespan taskdetails.list.0.Efforts.Estimated }} Example: "payload": "{\"timetracking\": {\"originalEstimate\": {{ timespan taskdetails.list.0.Efforts.Estimated }} }" |
timespanFromSeconds |
Converts seconds to a time span. | {{ timespanFromSeconds singleworklog.timeSpentSeconds 'hh\\:mm'}} Example: "payload": "{\"ReportedHours\": \"{{ timespanFromSeconds singleworklog.timeSpentSeconds 'hh\\:mm'}}\" }" |
int |
Converts a string to an integer. | {{ int '42' }} |
Value replacements | ||
map |
Replaces a mapped value with its equivalent, as defined in the field mapping. | {{ map config.mapping.projecttype.external singlejiraproject.projectCategory.id }} Example: "payload": "{\"TypeId\": {{ map config.mapping.projecttype.external singlejiraproject.projectCategory.id }} }" |
mapArray |
Maps an array of values. | {{ mapArray customfields 'Name' 'Jira Link' 'Id' }} Example: "payload": "{\"CustomField\": [{ \"CustomFieldBaseId\": {{ mapArray customfields 'Name' 'Jira Link' 'Id'}} }] }" |
StringReplace |
Replaces text in a string. | {{ StringReplace input.description "old" "new" }} |
DoubleQuotesReplace |
Escapes double quotes as " . |
{{ DoubleQuotesReplace singlejiraissue.fields.summary }} Example: "payload": "{\"Name\": \"{{ DoubleQuotesReplace singlejiraissue.fields.summary }}\" }" |
json |
Serializes an object to JSON. | {{{ json input }}} Example: "payload": {{{ json input }}}" (sends the full object as JSON) |
Conditionals | ||
ifempty |
Executes a block if the value is empty. | {{#ifempty context.LastExecution}}No last execution{{else}}Last executed{{/ifempty}} Example: "payload": "{\"jql\": \"... {{#ifempty context.LastExecution}}...{{else}}...{{/ifempty}}\" }" |
ifNull |
Executes a block if the value is null. | {{#ifNull input.assignee}}Unassigned{{/ifNull}} |
ifEquals |
Executes a block if two values are equal. | {{#ifEquals input.status 'closed'}}Closed{{/ifEquals}} Example: "payload": \"EndDate\": {{#ifEquals singlejiraissue.fields.duedate '' }}\"{{DateTime.Now \"yyyy-MM-dd\"}}\"{{else}}\"{{ date singlejiraissue.fields.duedate 'yyyy-MM-dd'}}\"{{/ifEquals}}" |
ifNotEquals |
Executes a block if two values are not equal. | {{#ifNotEquals input.status 'open'}}Not open{{/ifNotEquals}} |
ifext |
Executes a block based on a custom boolean expression. | {{#ifext (input.priority > 3)}}High priority{{/ifext}} |
Note: Always use triple braces {{{ ... }}}
with json
to avoid escaping quotes in JSON payloads.
Mustache syntax won't work. To access the first item in a array, instead of something like taskTeam.list.[0].Team
, it will have to be: taskTeam.list.First.Team
To compare or operate with values, you need to specify the type of value. Learn by example
{
"condition": "Convert.ToInt32(taskdetails.total) > 0 && Convert.ToInt32(taskdetails.list.0.KindId) == 3"
}
Guide
To perform logical operations, you need to specify the type of value. The script interpreter will throw an error otherwise:
There are the data converions you can use
Convert.ToBoolean()
: Converts a specified value to an equivalent Boolean value.Convert.ToDateTime()
: Converts a specified value to a DateTime value.Convert.ToDecimal ()
: Converts a specified value to a decimal number.Convert.ToDouble()
: Converts a specified value to a double-precision floating-point number.Convert.ToInt16()
:Converts a specified value to a 16-bit signed integer.Convert.ToInt32()
:Converts a specified value to a 32-bit signed integer.Convert.ToInt64()
:Converts a specified value to a 64-bit signed integer.Convert.ToSingle()
: Converts a specified value to a single-precision floating-point number.Convert.ToString()
: Converts the specified value to its equivalent string representation.
Learn by example
{
"url": "@@ITMAPI@@/my-company/projects/",
}
Guide
The extension interpreter provides you with variables that you can use. In the example above, we used @@ITMAPI@@
to call ITM Platform's login endpoint. The outcome of this case would be: https://api.itmplatform.com/my-company/projects
Reference
@@AccountId@@
: The account Id, such as18137
@@AccountName@@
: The account name as it appears in the URL. For examplemy-company
@@LanguageId@@
: The user's language code, as follows: en:1
, es:2
, pt:3
@@ITMAPI@@
: The host part of the URL.https://api.itmplatform.com/
Let's take the URL as an example.
Learn by example
{
"url": "@@ITMAPI@@/@@AccountName@@/login/{{ config.apikey }}",
}
Guide
In the example above, we are using @@variables@@ and {{ template syntax }} to call ITM Platform's login endpoint. The outcome in this case will be this: https://api.itmplatform.com/my-company/login/86d4bb2a-2129-40f5-8311-918c6da16823
To debug an extension, you need to look in the logs to know what happened during its execution. Because the script gets interpreted in ITM Platform's servers, there is no "run and debug" action as you may be used to in other languages.
When an extension is triggered, either by the scheduler or an event, two different types of logs are fed.
- The "Event History" displays a registry of executions, their status and useful information for the end-user.
- The "Script logs" are available for developers, and they show a registry of all action results.
To create and deploy a script, you must have the proper license and permissions within ITM Platform.
- From the Extension Panel, click on New Extension
- Fill out the name and other metadata. The extension name will be used internally as the unique identifier and cannot be changed. For example, 'my-extension'. It is case insensitive. No special characters or spaces are allowed
- Create the script in the "Code Editor" section
- Activate the extension from the Configuration section
- Some fields admit different language versions that will the displayed to the end-user depending on their language configuration. The three accepted languages are English (en), Spanish (es), Portuguese (pt). The default language is English.
- Extension: Apiece of code that adds a specific feature to ITM Platform
- Connector: An extension that connects ITM Platform with other applications
- Extension Script: Code that defines the behavior of the extension (represented within a JSON structure)
- Extension Interpreter: The module within ITM Platform that executes the extension script
- Entities: A singular object within ITM Platform. For example, a project or a task
- System Events: Signals emitted upon changes within ITM Platform. For example, a task update.
- Scheduler Events: Time-based system that specifies a moment in time. For example, every two hours.
- Trigger: Any occurrence, such as a system event or a scheduler event.
- Extension Features: Set of related actions that will be initiated upon a trigger.
- Actions: Pieces of code executed to implement a feature, typically chained to one another.
{"actions":[{
"action": "restcall",
"url": "https://api.itmplatform.com/myCompany/login/{{ config.apikey }}",
"method": "POST",
"description": "Login with apikey",
"output": "logininfo",
"payload": "",
"dataType": "application/json"
},
{
"action": "restcall",
"url": "https://api.itmplatform.com/myCompany/projects/22333",
"method": "GET",
"token": "{{ logininfo.Token }}",
"description": "Retrieve project",
"output": "project",
"payload": "",
"dataType": "application/json"
}]}
Result object in the logininfo
variable (output
of the first action):
{
"Token": "5955520211116162058986",
"UserID": "33989",
"AccountID": "18137",
"Result": "1",
"ResultStatus": "Success"
}