Commands and Variables
Developer commands can execute custom logic to make API requests, parse API responses, and combine multiple commands together.
Each command is described below along with a few examples.
The !jq
command is used to manipulate JSON using jq
expressions. jq
is an open-source query language for JSON. The output from a !jq
command can serve as input into other commands or steps in an integration.
Recommended resources for learning more:
- An Introduction to JQ: an excellent article on learning
jq
- jqplay: an online interactive tool for editing and testing
jq
expressions
The !http
is used to make API requests to the system you are integrating.
Warning
Authenticated API Requests
When a user connects an integration with a service provider, we utilize the specified authentication method as indicated in the
auth
object of the configuration file.If
auth
is configured asoauth2
, the App Studio service will continue to use the same access token created when the customer connected the integration. If the access token needs to be refreshed, the App Studio service will attempt refresh the token up to three times. If the token fails to refresh after three attempts, an error is recorded and the customer is notified.
An !http
command is defined by the following properties:
Key | JSON Type | Required | Default Value |
---|---|---|---|
path | string | no | |
url | string | no | |
target | string | no | "connection" |
method | string | yes | "GET" |
body | object | no | {} (empty object) |
params | object | no | {} (empty object) |
headers | object | no | {} (empty object) |
This is the URL path that is relative to the base_url value.
This is the full URL path for the request, if used instead of "path", it disregards the base_url value.
The target
property is used to specify the API to send the HTTP request. The three possible values are "connection"
, "activecampaign"
, and "external"
. When the value is set to "connection"
, the request will be sent to the external API URL specified in the api
object, using the authorization provided in the "auth"
section of the config. When the value is set to "external"
, an HTTP call is sent without using the connected authorization/"auth"
object. When the value is set to "activecampaign"
the request will be sent to the current ActiveCampaign account. All v3 API endpoints are supported with the exception of events and site tracking. If the target
property is not specified, the default value is "connection"
.
{
"!http": {
"target": "activecampaign",
"method": "GET",
"path": "api/3/tags"
}
}
Rules for Targeting ActiveCampaign APIs
When the
target
property is set to"activecampaign"
, the following rules apply.
- The
path
must be a relative URL path to a valid ActiveCampaign API endpoint. Thepath
cannot contain the host name (e.g.https://your-domain.activehosted.com
).- All v3 API endpoints are supported with the exception of events and site tracking.
The method
is the HTTP method that should be used when making an HTTP request to the API endpoint. The method
is limited to the following values: GET, POST, PUT, PATCH, DELETE, and OPTIONS.
The body
is the body of an HTTP request as JSON.
Warning
Currently, App Studio only supports valid JSON as the
body
of a request.
In this example, we are using the body
object to post JSON to a webhook resource.
"!http": {
"path": "repos/${resource::id}/hooks",
"method": "POST",
"body": {
"enabled": true
}
}
The params
are key/value pairs that are appended to the path of an HTTP request. These are often referred to as query string parameters.
In the following example, the param
object is used to include a sort
query string parameter with the request. The resulting path would be repos?sort=desc
.
"!http": {
"path": "repos",
"method": "GET",
"params": {
"sort": "desc"
}
}
Any properties defined in the headers
object will be added to the headers
of the HTTP request.
In the following example, the headers
object is used to specify the content-type
of the request.
"!http": {
"path": "repos",
"method": "GET",
"headers": {
"content-type": "application/json"
}
}
Most often the response of an !http
command will be piped to a !jq
as part of a !pipe
command to map the response to a specific format. The entire response of an !http
command can be referenced using .body
. Imagine the response from your API (internally to App Studio) looks something like the following.
{
"body": // the response from your API
}
Note: Use .body for APIs that return Arrays
If your API returns a top-level array, the
.body[]
syntax is required to properly handle the response.
The !pipe
command allows multiple commands to be chained in specific order.
When commands are chained, the output of one command becomes the input to the next command.
"!pipe": [
{
"!http": {
"path": "repos",
"method": "GET"
}
},
{
"!jq": ".body.repositories | map( { name: .name, id: .id } )"
}
]
In the previous example, the result of the !http
command is passed to a !jq
command. The result is to map the response's repositories
array and return a more simple array of objects with only name
and id
properties.
When you need to access output from a command that's not immediately before the current command, you can use the variable syntax ${piped_content.<index>}
where <index>
is the position of the command. From the previous !pipe
command example,
${piped_content.0}
refers to the input data for the pipe command${piped_content.1}
refers to the output of the!http
command${piped_content.2}
refers to the output of the!jq
command
All ${piped_content}
refer to outputs generated immediately before them.
The !print_c
command is intended to help with the development and debugging of your application. When executed, the !print_c
command will create a new App Studio Log entry with the command's string value. Variable syntax, such as ${piped_content.0}
is also supported. The !print_c
command does not impact data flow. This means that when !print_c
is used in a !pipe
command, the data output from !print_c
will be the same as the input.
"!pipe": [
{
"!print_c": "Starting HTTP Request"
},
{
"!http": {
"path": "contact",
"method": "GET"
}
},
{
"!print_c": "HTTP Result: ${piped_content.2}"
}
]
This example will result in two App Studio Log entries. The first will be a simple string "Starting HTTP Request". The other log entry will be a string that starts with "HTTP Result: " and then contains the JSON output from the !http
command, which was referenced with the variable ${piped_content.2}
.
Using
!print_c
for debuggingUsing
print_c
with the variable syntax can be helpful when debugging your application, as it can help you understand exactly what data is being held in a variable at a given point in your app's execution.
The log entries will appear in the App Studio Logs tab as follows:

The !extract
command allows developers to extract JSON strings from incoming webhook payloads and parse them into JSON objects.
Usage:
{
"!extract": {
"item_keys": list[string] // the field(s) containing the JSON to be extracted
"to_json": boolean // whether the fields need to be converted from a JSON string
}
}
Examples:
This is an example application/x-www-form-urlencoded
payload from Unbounce:
"page_url": "http://unbouncepages.com/wanderpath-2898/",
"page_name": "WanderPath",
"page_id": "69e2421e-df14-4196-89c5-8adf5ac0676c",
"variant": "a",
"data.json": "{\"email\":[\"[email protected]\"],\"first_name\":[\"Jane\"],\"last_name\":[\"Doe\"],\"phone_number\":[\"5555555555\"],\"variant\":[\"a\"]}",
"data.xml": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><form_data><email>[email protected]</email><first_name>Jane</first_name><last_name>Doe</last_name><phone_number>5555555555</phone_number><variant>a</variant></form_data>"
An example data_pipeline
that extracts the data.json
string and serializes it to JSON when processing looks like this:
"data_pipeline": {
"source": {
"!pipe": [
{
"!extract": {
"item_keys": [
"data.json"
],
"to_json": true
}
},
{
"!jq": ".[] | to_entries as $t | [$t[] | select(.value[]!=\"\") | {key, value: .value[]}] | from_entries | select(.variant!=null) | .+ {_tags: [.variant]}"
}
]
},
"target": {
"!resource": "ActiveCampaignContact"
}
}
The !resource
command allows developers to use resources within ActiveCampaign. Currently supported resources:
- ActiveCampaignContact
To get specific information about an ActiveCampaign resource, such as what fields are available for contacts:
"!resource": "ActiveCampaignContact.fields"
To refer to concrete instances of contacts:
"!resource": "ActiveCampaignContact"
ActiveCampaign Resources
This list describes what can be used with the !resource
command.
ActiveCampaignContact
This resource can access contact data for an ActiveCampaign account. Below are available features.
Available Contact Fields
Usage:
"!resource": "ActiveCampaignContact.fields"
Output Example:
[
{
"value": "email",
"display": "Email"
},
{
"value": "firstName",
"display": "First Name"
},
{
"value": "lastName",
"display": "Last Name"
},
{
"value": "phone",
"display": "Phone"
}
]
Contact Object
Usage:
"!resource": "ActiveCampaignContact"
Output Example:
{
"email": "[email protected]",
"firstName": "Edward",
"lastName": "Teach",
"phone": "(123)456-7890"
}
The !switch
command is similar to “switch” or “case” statements in many programming languages. It accepts an expression to evaluate and a series of “cases”, only one of which will be executed based on the output of the evaluated expression.
The !switch
command has two properties:
jq
: The expression to evaluate that will determine which case gets executed. This should be ajq
expression that operates on the output of the command just before the!switch
. The output of thejq
command must be the 0-based index of the case that should be executed.cases
: A list of commands, only one of which will be executed based on the output of thejq
property.
Examples
The following is a very simple example of the !switch
statement to show how it works.
{
"!pipe": [
{
"!jq": "${custom_data.user_selected_option.value}"
},
{
"!switch": {
"jq": "if (. | tonumber) > 3 then 0 else 1 end",
"cases": [
{
"!jq": "{message: \"This will be returned if ${expression} is true\"}"
},
{
"!jq": "{message: \"This will be returned if ${expression} is false\"}"
}
]
}
}
]
}
A common use of the !switch
command is to determine if a record should be created or updated, based on whether it already exists in an external system.
{
"data_pipeline": {
"source": {
"!resource": "ActiveCampaignContact"
},
"target": {
"!pipe": [
{
"!http": {
"method": "GET",
"path": "/contacts?email=${piped_content.0.email}"
}
},
{
"!switch": {
"jq": "if .body.meta.total >= 1 then 1 else 0 end",
"cases": [
{
"!http": {
"method": "POST",
"path": "/contacts",
"body": "${piped_content::0}"
}
},
{
"!http": {
"method": "PUT",
"url": "/contacts/${piped_content::1.records[0].id}",
"body": "${piped_content::0}"
}
}
]
}
}
]
}
}
Within your configuration, you may need to store data to variables for later use. These can be saved in our system with the !save
command. They can then can be referenced elsewhere in your configuration using data substitution variables.
"!save": {"scope": "CHANGEME"}
The scope value should be the same as the data_intake scope set in the data_intake
of the configuration. It can be either workflow, connection or application.
The following example uses the !save
command to store a the returned webhook id
as a variable named webhook_id
. This webhook_id
value is needed to later in the application lifecycle to update or delete the webhook.
{
"!pipe": [
{
"!http": {
"method": "POST",
"path": "/change/me"
}
},
{
"!jq": ".body | {webhook_id: .id}"
},
{
"!save": {
"scope": "workflow"
}
}
]
}
The !save
command will save the !jq
output to the App Studio system. Later, it can be access using the following variable.
${data.workflow.webhook_id}
The !error
command can be used to halt the execution of a pipeline and log a message. The command only supports a static string.
{
"!error": "A helpful error message"
}
All values entered by users in the UI elements defined by the select
step of a workflow are saved in our system. They can be referenced elsewhere in your configuration using custom_data
substitution variables.
Accessing a value stored in the custom_data
object has the following format, where <id>
is the id
of the select
form field and <property>
is the name of the property to access (usually value
).
${custom_data.<id>.<property>}
Each custom data reference MUST follow this format: surrounded by an opening {
and closing }
curly braces, and prefixed with a dollar sign $
.
Custom data are stored in a simplified data structure. Since they represent user input, each entry has two keys:
value
: this is the value of user input, it's guaranteed to always existdisplay
: this is the label or display users saw when they made a selection in the UI, it exists for all UI elements but "text"
A simplified example of this data structure would look similar to the following.
{
"dropdown-input-1": {
"display": "Apple",
"value": "apple"
},
"text-area-1": {
"display": "",
"value": "user input as text"
}
}
To access the display of dropdown-input-1
, you would use:
${custom_data.dropdown-input-1.display}
To access the value of text-area-1
, you would use:
${custom_data.text-area-1.value}
The domain for some APIs may vary by subdomain based on the account or organization using the API. The api
and auth
objects support using a ${subdomain}
as part of the URL path. Using the variable will not only prompt customers to enter their specific subdomain during the application setup process, you can also access the value entered by the customer at any other step in the config using the ${subdomain}
variable.
There are two options when displaying the subdomain input in the UI.
If the base_url
in the configuration includes the ${subdomain}
substitution variable then the input will appear with the base_url
parts wrapping the input:

Otherwise the input will be shown as:

In the following example, only a portion of a configuration is shown.
{
"$version": "2",
"api": {
"base_url": "https://${subdomain}.example.com"
},
"auth": {
"my_oauth2_configuration": {
"type": "oauth2",
"configuration": {
"authorization_base_url": "https://api.example.com/oauth2/authorize",
"client_id": "woef2qo3hefawWEWrfh2qe21wlvdflkefnqs",
"client_secret": "[insert your secret here]",
"scopes": [
"insert_scope_here_if_needed"
],
"token_url": "https://api.example.com/oauth/token",
"refresh_url": "https://api.example.com/oauth/authorize"
},
"defined_fields": {
"subdomain": {
"label": "Account Subdomain",
"placeholder": "Enter your Account Subdomain",
"help_text": "Your subdomain immediately precedes the .example.com portion of your Example url."
}
}
}
}
}
As can be seen in the previous example, the ${subdomain}
variable is being used to replace a portion of the base_url
. The ${subdomain}
variable can be used throughout your configuration.
Updated 11 months ago