Node.js: Create and Test Your First App using Pipedream
Get started on your first ActiveCampaign CX App using Node.js
App Studio is used to build native applications and integrations for the ActiveCampaign platform. We call them CX Apps. CX is short for "Customer Experience." A CX App built with App Studio can be published privately (for internal use by your organization) or publicly in the ActiveCampaign App Ecosystem.
In this tutorial, you will learn how to use App Studio to build an outbound CX App and test the app using Pipedream. Pipedream is a powerful serverless platform for connecting systems. Pipedream can be used to prototype APIs, test integrations, host webhooks, or build production apps and integrations. Outbound CX Apps are used as actions in Automations to send data from ActiveCampaign to other systems.
Tutorial Overview
In this tutorial, you will learn to create the following.
- Create a mocked API in Pipedream using Node.js and JavaScript. Your API will authenticate a login, return data to populate a dynamic list of options, and receive contact information from ActiveCampaign.
- Create an app in App Studio. You will use create a JSON configuration file that describes how ActiveCampaign will interact with your API.
- Create an Automation to test the app. By creating an Automation, you will see how an ActiveCampaign customer would configure your app, and you will be able to inspect the data that is sent from ActiveCampaign to the API as part of the app lifecycle.
Prerequisites
- A free ActiveCampaign sandbox account
- A free Pipedream account
Create an App in App Studio
Login to your ActiveCampaign sandbox account. Click Apps and then App Studio. Click Build a New App.
Complete the Create New App form using your email address and click Submit.
Click the Home tab. Click Start Building.
CX Apps built with App Studio are configured in the App Configuration Editor. An app config is a JSON document that conforms to the App Studio Configuration Specification.
Create a Pipedream Workflow
In this step, you will use Pipedream to create a mock API for your CX App to connect and interact. First, create a new workflow in Pipedream. This workflow will become the API for your CX App.
Next, select the HTTP / Webhook Requests trigger for the workflow.
Edit the trigger to Return a custom response from your workflow
You now have a URL you can use for your App, but it does not do anything, yet. Click on the plus (+) to add a step to the workflow.
Click on Run custom code.
Enter the following code inside the existing async
code block.
await $.respond( {
status: 200,
body: {
id: "123472",
name: "Taylor Jones"
}
});
Your Pipedream step should now look like the following.
export default defineComponent({
async run({ steps, $ }) {
await $.respond( {
status: 200,
headers: {"Content-Type": "application/json"},
body: {
id: "123472",
name: "Taylor Jones"
}
});
return steps.trigger.event
},
})
Next, test the API code you just added. Click the Test button to validate there are no errors, and then the Deploy button. Copy the URL assigned to this Pipedream URL. Open another browser window or tab, paste the URL, and press Enter.
Remember to deploy any change you make in Pipedream!
You should see the following data returned in your browser window. This response is an example of what ActiveCampaign expects to receive when authenticating with your API.
{"id":"123472","name":"Taylor Jones"}
Next, complete the Pipedream API. Copy and replace everything in the Pipedream workflow with the following code, and click Save.
export default defineComponent({ props: {
username: {
type: "string",
label: "Username",
},
password: {
type: "string",
label: "Password",
secret: true
},
accountId: {
type: "string",
label: "AccountId",
},
name: {
type: "string",
label: "Name",
}
},
async run( { steps, $ } ) {
async function unauthorized( msg ) {
await $.respond({
status: 401
});
// log the error message
$.flow.exit(msg)
}
// Validate Basic Authentication
function validateBasicAuth( authorization, username, password ) {
if (!authorization) {
return {
isValid: false,
error: "No Authorization header present"
};
}
const auth = Buffer.from(authorization.split(" ")[1] || "", 'base64').toString();
if (!auth) {
return {
isValid: false,
error: "No data in Authorization header"
};
}
const [user, pass] = auth.split(":")
if (user !== username || pass !== password) {
return {
isValid: false,
error: "Username or password does not match"
};
}
return {
isValid: true,
error: ""
};
}
// Get the path from the raw URI
const path = steps.trigger.event.path;
const method = steps.trigger.event.method;
const authHeader = steps.trigger.event.headers.authorization;
const { isValid, error } = await validateBasicAuth(authHeader, this.username, this.password);
if (!isValid) {
return unauthorized(error);
}
// GET /me request handler
if ( path === "/me" && method === "GET" ) {
return await $.respond({
status: 200,
body: {
id: this.accountId,
name: this.name
}
});
}
// GET /options request handler
if ( path === "/options" && method === "GET" ) {
return await $.respond({
status: 200,
body: {
options: [{
id: 1,
title: "Option 1"
}, {
id: 2,
title: "Option 2"
}, {
id: 3,
title: "Option 3"
}]
}
});
}
// GET /fields request handler
if ( path === "/fields" && method === "GET" ) {
return await $.respond( {
status: 200,
body: {
fields: [ {
field_id: "first_name",
label: "First Name"
}, {
field_id: "last_name",
label: "Last Name"
}, {
field_id: "email_address",
label: "Email Address"
}, {
field_id: "phone_number",
label: "Phone Number"
}, {
field_id: "custom_field_1",
label: "Custom Field 1"
}, {
field_id: "custom_field_2",
label: "Custom Field 2"
}
]
}
} );
}
// POST /contact request handler
if (path === "/contact" && method === "POST") {
return await $.respond({
status: 200,
body: steps.trigger.event.body
});
}
// Otherwise respond with a 404 - Not found
await $.respond({
status: 404
});
return $.flow.exit(`404 - ${ steps.trigger.event.method } ${ steps.trigger.event.url }`);
},
})
If you are familiar with JavaScript or other programming languages, you may be able to follow the code logic. However, it's totally okay if you don't understand all of the code. The features of the API include the following.
- The API uses Basic Authentication to validate every request. Any request that does not include a valid username and password will be rejected with a standard HTTP 401 error.
- An API request sent to
/me
will return a JSON object that includes an account ID and username. - An API request sent to
/options
will return a JSON object that includes an array of options. - An API request sent to
/fields
will return a JSON object with afields
array for the mapping step. - JSON data sent to
/contact
will respond with the same data.
You should now see a section named Configuration on the left. You can enter any values you wish into these fields. For example, the values you enter for the Username and Password will become the credentials you must enter when you test your CX App in ActiveCampaign. The AccountId and Name are the values the /me
API will return with a successful authentication. When you're finished editing the Configuration, click Deploy.
Update the App Config in App Studio
Copy the following JSON and replace the JSON currently in the App Configuration Editor.
{
"$version": "2",
"api": {
"base_url": "{{your-pipedream-url}}"
},
"auth": {
"basic-auth": {
"type": "basic",
"verify_url": "{{your-pipedream-url}}/me",
"defined_fields": {
"username": {
"label": "Username",
"placeholder": "Enter the username for your account",
"help_text": "This is the username you entered as a param in Pipedream"
},
"password": {
"label": "Password",
"placeholder": "Enter your password",
"help_text": "This is the password you entered as a param in Pipedream"
}
}
}
},
"workflows": [
{
"name": "send-a-contact-to-pipedream",
"label": "Send a contact to Pipedream",
"description": "Example of sending a contact to Pipedream",
"type": "automations",
"auth": "basic-auth",
"setup": {
"connect": {
"label": "Connect",
"describe_connection": {
"!pipe": [
{
"!http": {
"method": "GET",
"path": "/me",
"headers": {
"accept": "application/json"
}
}
},
{
"!jq": "{ account_id: .id, description: .name }"
}
]
}
},
"select": {
"label": "Outbound Test Settings",
"form_fields": [
{
"id": "dropdown-example",
"label": "Dropdown Example",
"type": "dropdown",
"required": true,
"options": {
"!pipe": [
{
"!http": {
"method": "GET",
"path": "/options"
}
},
{
"!jq": ".body.options | map({ display: .title, value: .id | tostring })"
}
]
}
},
{
"id": "multiselect-example",
"label": "Multiselect Example",
"type": "multiselect",
"required": false,
"options": {
"!jq": "[ { display: \"Option A\", value: \"A\" }, { display: \"Option B\", value: \"B\" }, { display: \"Option C\", value: \"C\" } ]"
}
},
{
"id": "textarea-example",
"label": "Textarea Example",
"type": "textarea",
"required": false,
"personalize": "ActiveCampaignContact",
"placeholder": "Enter your text here"
}
]
},
"map": {
"label": "Mapping",
"describe_source": {
"label": "ActiveCampaign",
"options": {
"!resource": "ActiveCampaignContact.fields"
}
},
"describe_target": {
"label": "Pipedream Contact",
"options": {
"!pipe": [
{
"!http": {
"method": "GET",
"path": "/fields"
}
},
{
"!jq": "[\"first_name\", \"email_address\"] as $required | .body.fields | map( .field_id as $id | { id: .field_id, title: .label, required: (if ($required | index($id)) == null then false else true end)})"
}
]
}
}
}
},
"data_pipeline": {
"source": {
"!resource": "ActiveCampaignContact"
},
"target": {
"!pipe": [
{
"!http": {
"method": "POST",
"path": "/contact",
"body": {
"custom_data": "${custom_data}",
"dropdown-value": "${custom_data.dropdown-example.value}",
"textarea-value": "${custom_data.textarea-example.value | default}",
"multiselect-value": "${custom_data.multiselect-example | default}",
"contact": "${piped_content.0}",
"source_data": "${source_data}"
}
}
}
]
}
}
}
]
}
In the app config, there are two placeholders currently set to {{your-pipedream-url}}
. Go to your Pipedream workflow and copy the URL assigned to your workflow. Replace the two placeholders in App Studio with your personal Pipedream URL.
When you have finished updating the JSON in the App Configuration Editor, click Save and click Test App in Sandbox.
When you click the Test App in Sandbox button, you will be directed to your CX App's connections page. The first time you do this, there will be no connections listed. Once a connection is established you can return here to view a list of active connections. You can also find usage logs for your connected CX App here.
Any time you make a change in App Studio, click both the Save and the Test app in sandbox buttons. If you have your CX App open in another browser tab or window, refresh the page to ensure you are seeing the latest version of your app.
Create a New Automation
So far, you have created an API in Pipedream and CX App using App Studio. The next step is to create an Automation you will use to test the CX App. Click the Automations link in the navigation menu and then click the Create an Automation button.
Click on the Start from Scratch option then click the Continue button.
Click Start without a trigger. You will manually add a Contact to the Automation when you are ready to test it.
Click the + button to add a new action to the Automation.
Click on the CX Apps tab and then click on your CX App.
When your CX App is added to the Automation you will need to establish a connection. This connection will use the authentication scheme defined in the auth
section of your CX App's config file. Enter the same Username and Password you set in the Pipedream workflow params section. Click the Connect button.
The username and password will be sent to your Pipedream API to verify it is valid. If the username and password do not match what is stored in Pipedream, you will see an error and you may retry the login.
Next, you will configure your outbound workflow. In the app config, three form fields were defined.
- A drop-down list that is populated by making a call to your
/options
API endpoint. - A multi-select list populated with a static list of options.
- A textarea field that can be personalized with contact data.
Pipedream The data entered here will be sent to your Pipedream API when the CX App's outbound workflow is executed. These fields were defined on the select
object for the workflow
in the App Config file.
Next, you will configure the mapping of ActiveCampaign fields to the outbound system. These fields were defined in the map
object for the workflow
in the App Config file. Mappings are optional, but this example is designed so you can see how the mappings work.
Enter a name for your automation then click the Save button. Then click the Active button before testing.
Test the Automation Using a Contact
Now that the Automation has been created and set to Active you can add a Contact to the Automation to test the CX App. Click on Contacts and then click on a Contact to go to the Contact Details page.
In the Details page for your Contact click the + Add button next to Automations. Select the Automation that you just created and then click Okay.
Once the Contact has been added to the Automation your workflow should have executed. Navigate back to your CX App's Connections page by clicking on Apps and then clicking on Connected Apps. In your CX App's Connections page click View Logs. You should see a log entry for the Contact that you added to the Automation in the last step.
Back in Pipedream you can also view the data that was sent to your API when the outbound workflow was executed.
If you have made it this far, congratulations! You have successfully created and tested a CX App using App Studio and Pipedream!
Next Steps
Feel free to make changes to your CX App configuration, such as lists and textarea, and re-run the Automation as many times as you wish. See how those changes affect the data posted to Pipedream. Explore and tweak the API code and the app config and see how that affects the CX App.
Pipedream is a powerful platform for building custom integrations. You may want to use Pipedream as the "glue" between ActiveCampaign and another system you wish to integrate. Or, continue to use Pipedream to test and mock any part of a CX App you are building whenever you need to inspect requests or data sent from ActiveCampaign.
Updated over 1 year ago