Only this pageAll pages
Powered by GitBook
Couldn't generate the PDF for 171 pages, generation stopped at 100.
Extend with 50 more pages.
1 of 100

Custom Apps Documentation

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

App structure

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Make Apps Editor

Develop apps in Make UI

The Make UI allows you to browse in Example and Custom apps you previously created.

When developing an app, you can work with elements such as base, connections, webhooks, modules, RPCs, and custom IML functions.

Discover how to develop apps using the available features in Make UI.:

Develop apps in VS Code extension

The VS Code extension supports all jobs available in the Make UI, additionally, it empowers developers with the following capabilities:

  • Importing & exporting the apps

  • Cloning components within the app

  • Support for apps' local development, enabling:

    • cloning Make apps to the local workspace and a repository, for example, git

    • development of multiple app versions, e.g. deploying one code to testing and production app

    • pulling updates or new app elements to the local workspace and a repository

    • versioning of the app

    • collaborative development

    • control over code contributions

    • local development and deploying local app code back to Make

Learn how to get the most out of the VS Code extension:

Custom Apps Documentation

Make Custom Apps documentation is a guide for developers looking to create their own apps for themselves or others to use on the Make platform. This documentation will walk you through how to use Make Apps Editor in Make UI and in Visual Studio Code to create and manage those custom apps, as well as best practices and common approaches for development.

Custom Apps Development Training

If you are new to custom apps on Make or want to freshen up your knowledge about apps developing, enroll in the . It has 39 lessons and 4.5 hours of video content where you can learn all the aspects of custom apps development on Make.

Introduction to creating custom apps

When there is a service that you want to use in Make but the service is not yet available in Make, use the apps builder to create a custom app. The only requirement is that the service has to have an API.

In the apps builder, you write down a JSON configuration. This configuration is then used by the Make platform to generate all connections and app modules for you. If you are working on a complicated custom app, you can write a custom IML function with JavaScript.

There are two options directly supported by Make to write the custom app configuration:

  • The web interface of your Make account instance.

  • The Visual Studio Code (VS Code) extension.

The benefits of using VS Code over the Make account web interface are for example:

  • first-class support for JSON format, like syntax highlighting and completion,

  • automatic checking of the JSON configuration validity, notably in terms of parameter type checking and correct object context,

  • predefined project structure for every custom app you create,

Follow the instructions to configure the VS Code extension.

If you want to write the custom app configuration in the Make web interface, navigate to Custom apps in the left sidebar menu of your Make account. You might have to click on the three dots at the bottom of the left sidebar to view the Custom apps option.

If you are developing a custom app for the first time, check out the section first.

Important notes

  • All modules can be tested directly in scenarios.

  • Changes to communication configs are immediately active.

  • You can see raw requests/responses in your browser's console.

  • Changes in parameters and interface requires you to reload scenario editor page.

  • We use JSONC (JSON with comments) in all sections except common data.

Collaborative development

The Make Apps Editor VS Code extension contains support for Git.

Learn how to utilize feature to develop .

Develop apps in Make UI
Develop apps in VS Code
Custom Apps Development Training
here
Create your first app
Local Development for Apps
collaboratively
Develop apps collaboratively

How to read the documentation

The Apps platform documentation is divided into topic groups. The topic groups are structured the same way as you would navigate inside the Apps builder in Make.

App structure

Section APP STRUCTURE describes how to define and configure the basic elements of a custom app. The basic custom app elements are listed in the top menu in the custom app configuration, for example: Base, Communication, Modules .

Where do I find it?

Go to Menu -> Custom apps tab -> Open your custom app.

App Structure

App blocks

Every structure element is divided into blocks. Some of these blocks appear in multiple places and share the same purpose, others do not.

Parameters (static or mappable) are available everywhere apart from Base and Functions. Meanwhile, Common data exists only inside Base and Connection.

App Blocks

App components

COMPONENTS are like bricks for APP BLOCKS. Block doesn't exist without components. Multiple components form a block.

Pick a brick that suits your needs and use it. You can use multiple separated components or combine them together by using the nestedproperty. Play around and find the best components combination for you!

Module example
App Components

When for a call you need to provide a name and an email -> you would use parameters "type": "email" and "type": "text".

[
    {
        "name": "email",
        "label": "Email",
        "type": "email"
    },
    {
        "name": "name",
        "label": "Name",
        "type": "text"
    }
]

When for a call in case X you need to provide a name and in case Y an email-> you would use parameters "type": "select" with nested parameters "type": "email"/ "type": "text" under each option.

[
    {
        "name": "select",
        "label": "Select",
        "type": "select",
        "options": [
            {
                "label": "Case X",
                "value": "caseX",
                "nested": [
                    {
                        "name": "email",
                        "label": "Email",
                        "type": "email"
                    }
                ]
            },
            {
                "label": "Case Y",
                "value": "caseY",
                "nested": [
                    {
                        "name": "name",
                        "label": "Name",
                        "type": "text"
                    }
                ]
            }
        ]
    }
]

Develop apps in Make UI

When you create your app in the Make platform, you are using the web code editor. The web code editor has a bunch of handy features to make your app development easier:

  1. Hints with links to the apps platform documentation with more details.

  2. Code reformat button with a label showing the data format. The available editor box data formats are:

    • jsonc

    • json

    • javascript

    • markdown

  3. Context aware code suggestions for the jsonc data format. The web code editor gives you hints what parameters or properties you can add to the custom app code. The code suggestions work even for RPCs.

  4. Hints displayed when hovering over the custom app configuration.

  5. Custom app code validation for the jsonc data format. If you misspell a code property name, or if you place a property in an invalid code section, the web code editor highlights the error.

  6. One button to save changes in all editor boxes on the page.

  1. Hovering over shows their docs.

  1. Hovering over provides a link to the function definition.

  1. Controls to expand and collapse the code. To view the controls, hover over the space between your code and editor line numbers.

  1. The editor highlights the IML syntax so it's distinct from the rest of the code.

Web code editor keybindings

The web code editor is built on the same backend as the Visual Studio Code. If you know your way around Visual Studio Code, you will find that some keybindings also work in the web code editor. Some of the most useful web code editor keybindings are:

For Windows users:

  • Ctrl + Shift + H: shows a cheat sheet with selected shortcuts

  • Ctrl + S: save your changes to all of the editor boxes on the page

  • Ctrl + F: search in the editor box content

  • Ctrl + H: search and replace in the editor box content

  • Ctrl + Space: show a list of code suggestions valid in the current cursor context

  • Shift + Alt + F: format code

  • Ctrl + /: toggle line comments

  • F1: display web code editor command and keybindings reference

For MacOS users:

  • Ctrl + Shift + H: shows a cheat sheet with selected shortcuts

  • Cmd + S: save your changes to all of the editor boxes on the page

  • Cmd + F: search in the editor box content

  • Opt + Cmd + F: search and replace in the editor box content

  • Ctrl + Space: show a list of code suggestions valid in the current cursor context

  • Shift + Opt + F: format code

  • F1: display web code editor command and keybindings reference

For a full reference of the web code editor keybindings check the official and the official VSCode keybindings cards:

Note that your browser might intercept some keybindings in the official reference or might not make sense in the context of a web code editor. For example, the keybinding Ctrl + N creates a new file in VSCode, but in Google Chrome the keybinding creates a new browser window instead.

Develop apps in VS Code

You can get the Make Apps Editor extension from the .

Make supports writing custom apps in Visual Studio Code (VS Code) with the Make Apps Editor extension. You can get the VS Code extension from the or install it in the Extensions tab in VS Code.

You write and edit the custom app configuration with the VS Code extension as you would edit a JSON file on your PC. The custom app configuration is downloaded to your PC when you open it from VS Code and uploaded to Make when you save it. The custom app configuration is stored on your PC as temporary files. The temporary files are deleted after you close the configuration.

The custom app configuration is associated with your account in Make. The communication with the VS Code extension and your Make account is authorized with an API key. To set up the VS Code extension you must provide an API key with the appropriate API key scopes. Generate a new Make API key if you don't have one:

If you come across any bugs in the Make Apps Editor, please don't hesitate to contact our support team.

built-in IML functions
custom IML functions
VSCode documentation
MacOS
Windows
Make apps platform web code editor with callouts.
Hovering over built-in IML function
Hovering over custom IML function
Colapsing the code
VS Code Marketplace
VS Code Marketplace
Generate your API key

Best practices

This document outlines the standard practices and naming conventions that all app developers should adhere to in order to maintain consistency within Make.

Local development for Apps

Tools

The Tools section features useful tools that can help you build your scenario. Detailed descriptions for each tool are available in the Make documentation.

Create an app in VS Code

  1. When you have successfully logged in and have the environment set, it's time to develop your first app. To start, click the + icon in the header of My apps section or call the >New app command directly from the command palette.

  1. First, you will be asked to fill in a label. That's the app's name the users will see in the Scenario builder.

  1. Next, the app ID will be generated for you. It will be used in URL paths. However, it should be clear to which app it leads. It has to match (^[a-z][0-9a-z-]+[0-9a-z]$) regular expression.

  1. Then, you'll be asked to enter the app's version. Currently, the version 1 is only supported.

  1. Next, you'll be prompted to enter the description of your app.

  1. Then, enter a color theme for your app. This is the color the app's modules will be seen as in scenarios. The theme is in hexadecimal format. For example, the Make app's color is #6e21cc.

  1. The next prompt will ask for the app language. That is the language of the interface of the app. Most of the apps in Make are currently in English.

  1. The last prompt is for countries where the app will be available. If you don't pick any country, the app will be considered global.

Currently, this selection does not affect the availability of the app.

And that is it. After you confirm the last dialog, your brand-new app will appear in the My Apps view and you can start coding.

Develop apps collaboratively

The collaborative development is facilitated by the Git for apps feature in the VS Code extension. The supported operations are documented in this .

Prerequisites

To start the collaborative development, ensure that you have set up the testing and production app versions by following this article.

Every collaborating developer should have their testing app in Make, connected to the local app from the Git repository.

Roles

  • Owner of the production app - Every app in Make can be owned by a single Make account. The owner of the production app manages the deployment of the new local app version to the Make app.

  • Developers of testing apps - Each developer manages their own testing app in Make, which is connected to the local app from the Git repository.

Collaborative development flow

Below is a diagram explaining how developers can collaborate on app development.

Deploy changes from local app to Make app

The changes in components or new components can be partially deployed to Make, or the app as a whole can be deployed to Make.

  1. Follow the instruction, that fits your case, below.

To deploy only a specific code file in a component to Make, right-click the code file and select Deploy to Make (beta).

To deploy a component to Make, right-click the component directory and select Deploy to Make (beta).

To deploy the whole app to Make, right-click the makecomapp.json file and select Deploy to Make (beta).

  1. In the dialog, select the origin where the changes should be deployed.

  1. The changes or new components are now available in the Make app. The new app version can be thoroughly tested in Scenario Builder. If you utilize testing and production apps, you can deploy the changes or new components to the production, after the testing app passes the testing phase.

Tracking code changes

Tracking code changes in VS Code extension

Once there is a change saved in an approved app, the app is marked with * next to the name. Also, every module with its blocks, where the changes are available, is marked with the *.

To view what's changed right click the item and pick the Show changes option.

A compare view will appear and you'll see the changes that have been made.

Tracking code changes in web interface

Once there is a change saved in an approved app, a new tab Changes will appear. There is a list of all changes available in the particular app.

After clicking on a diff log, a page with the particular app block will open, with a dialog informing your about available changes. For a detail diff file, click Show diff button.

A compare view will appear and you'll see the changes that have been made.

Commit the changes in Git repository

Whenever you create a new package of changes, you can commit them to create a new version in the Git repository.

The manual describes the development in the Git repository using the app. Yet it's not obligatory, any preferred can be used.

  1. Go to the GitHub Desktop app and open the repository with the latest version of your app. You should see a list of new changes.

  2. Enter the Summary, optionally the Description of the commit, and click Commit to main.

  1. Now, the new version of your app is logged.

Static parameters

The Static Parameters tab is deprecated and should be left empty. All fields should be inside the Mappable parameters.

App's environment

Click on an app in the Apps list. You will get a page with nine tabs. In this guide, we will focus on the custom app's main components:

The App structure section describes the components of an app in detail. You can explore it later.

Now you know the main components of a Make app. Your next step is to set up the custom app Base.

Compare changes between local and Make app

Please be advised that this feature is in beta, meaning, it may encounter occasional bugs or inconsistencies, so proceed with an awareness of potential functionality limitations.

One feature available to developers is the "Compare with Make" functionality. This tool allows developers to compare the code in their local app with the code in their Make app.

By leveraging this feature, developers can easily identify differences, track changes, and ensure consistency between their local and Make apps.

To compare the code of a local component with a remote component, right-click the local component block and select Compare with Make (beta).

Once clicking, a view with the local code on the right side, and the remote code on the left side will appear.

Debugging of Custom IML Functions

Debug IML in Web Browser

Debug IML in VS Code

Create your first app

This tutorial will guide you through the steps to create a custom app with a module. You will use the Make "Virtual Library Demo API."

If you want to test the Virtual Library Demo API click the link:

A new tab opens in your web browser with the API response in JSON format:

The development of a custom app in Make is divided into several steps. These steps are described in detail in the following subpages.

If you struggle with finding the app API, search on a web search page for: API site:www.app-or-service.com. For example,

Debugging your app

During the development of your app, you will probably experience multiple issues with your app and/or API. Therefore, you will need to use Make DevTool and/or console in order to debug your app. Continue below in order to learn how!

Basics of debugging

You can easily debug your app using Make DevTool or Chrome DevTool's console. Learn how to use Make DevTool or Chrome DevTool's console below.

Debugging of pagination in list/search module

When the GET endpoint, which you are using, supports pagination, you need to make sure the module uses it correctly. Learn how you can test if your pagination works correctly below.

Debugging RPC

Make apps platform has a tool for testing the behavior of your RPCs. Thanks to this tool, testing of RPCs is easy. Learn, how to test your RPCs below.

Debugging of custom IML functions

Implementing a custom IML function might get complicated, therefore, you will need to learn, how to effectively debug your custom IML functions. Learn how below.

Private/Public apps

This section explains the work with updates in or apps.

Changes

In custom apps, which haven't been approved by Make, it is not possible to keep track of changes as there is no Diff tool available.

If you need to keep track of your changes and you are not planning to have your app approved by Make, you can export the current version of your app every time you make a change, using extension, and store the files on GitHub or any similar tool.

All changes take effect immediately. Before saving a change, make sure it will not negatively affect existing scenarios.

You must ensure not to have Javascript syntax warnings or errors on your custom IML functions in your app.

Otherwise, all scenarios that are using the app will throw the error message about Javascript syntax and will be stopped immediately.

This will affect all scenarios no matter if they run the app's modules that do not contain the faulty custom IML functions.

Versioning

If you find out, that there has been a new API version implemented, or there have been major changes in the current API made, you should create a new app.

This way, you will make sure that everything is consistent and that there are no breaking changes made in the current app.

Action

The Action Module is a simple module that makes a request (or several) and returns a result. It does not have a state or any internal complex logic.

Use if the API endpoint returns a single response.

Module Actions

Components

{"result":"Hello, World!"}
http://demo-api.integrokit.com/api/v1/helloworld
API site:www.eventbrite.com
private
public
VS Code
Section My Apps
Entering new app's label
Entering new app's name
Entering new app's version
Entering new app's description
Entering new app's theme
Selecting new app's language
Selecting countries.
New custom app in Make
Deploying a communication file of create-house module
Deploying a component directory of create-house module
Deploying app to Make
Selecting the app origin for deploying changes
App blocks marked by * char
Option Show changes
Diff file
Changes tab with list of changes
Show diff button in the dialog above the app's block
Example of a diff file
GitHub Desktop
GUI tool
Commiting new app version to continue the versioning of the app
Created new change log record
Wrongly Used Static Parameters Tab
Correctly Used Static Parameters Tab
Base
Connections
Modules
App's Environment
Compare with Make (beta) feature
Example of code comparison between Make app and local app

Base

Every custom app has a piece of settings that are common to all custom app modules and remote procedures. Use the BASE tab to specify settings that are inherited by all modules and remote procedures.

These common settings are:

  • Base URL

  • Authorization

  • Error Handling

  • Sanitization

Set up base for the demo API

In this step, we will set up the Base for our custom app. The custom app uses the Virtual Library Demo API.

  1. Navigate to your custom app settings.

  2. Click on the BASE tab.

  3. The first code block already contains a JSON snippet:

{
    "baseUrl": "https://www.example.com",
    "log": {
        "sanitize": ["request.headers.authorization"]
    }
}
  1. Replace the URL address https://www.example.com with the Virtual Library Demo API base URL: http://demo-api.integrokit.com/api/v1

  2. Press Ctrl+S to save changes.

Note that the baseUrl key contains the API URL without the /helloworld endpoint path. You specify the endpoint path in the module settings.

The base settings of your custom app look like this:

{
    "baseUrl": "http://demo-api.integrokit.com/api/v1",
    "log": {
        "sanitize": ["request.headers.authorization"]
    }
}

Approval of changes in approved app

Approval of changes in Make

Every change that has been made in an approved app has to be approved by Make.

Until then, the changes will not be effective in the public version of the app.

Once you decide that the changes, you have made so far, should be applied to the public version of your app as well, you will need to pass update review.

In order to do so, open the Review tab in your app, and update the form. Enter links to your updated scenarios with new logs, where we can see the functionality of updated modules, and/or links to new scenarios with logs, where we can see the functionality of your new modules. Then, submit the form by clicking Update review.

After submitting the form, you will receive a new e-mail confirming that we have logged your request. You can find the e-mail by subject's name: "App Update Review: YourApp'sName".

Our Senior Developers will check your changes. The changes can be either approved or rejected.

In case of approval, you will be informed by e-mail, that your changes have been approved.

While in case of refusal, our senior developer will contact you with further details and/or recommendations for the correct solutions.

What is considered a change that has to be approved by Make

  • changes in current connection, modules, RPCs, custom IML functions, webhooks

  • a new connection, modules, RPCs, custom IML functions, webhooks

  • new theme and/or logo of the app

Rollback of changes in Make

If you need to have all changes, that you have made so far, rollbacked, contact us via helpdesk.

Advanced inheritance

Consider this to be the base:

{
    "headers": {
        "authorization": "Bearer {{connection.accessToken}}"
    }
}

In a module, you need to add a custom header programmatically.

{
    "headers": "{{headerBuilderFunction()}}"
}

That results in the base being overwritten by the result from the IML function. To merge both collections, use this special IML syntax inside the module:

{
    "headers": {
        "{{...}}": "{{headerBuilderFunction()}}"
    }
}
section
Development flow diagram
Manage testing and production app versions
Create a new app origin
Debug IML in Web Browser
Debug IML in VS Code
Make DevTool
Debugging of pagination in list/search modules
Debugging RPC
Debugging of Custom IML Functions
Module Actions
Components

Configure VS Code

  1. Install the Make Apps Editor. You can get the Make Apps Editor from the VS Code Marketplace or install it in the Extensions tab in VS code.

  2. Click the Make icon on the VS code sidebar. Clicking on the Make icon activates the Make Apps Editor. You get notified in a pop-up window that you haven't yet set up a development environment.

Pop-up about missing environments
  1. Click the Add environment button to launch the environment setup or execute the command: >Make Apps: Add SDK Environment from the command palette.

Command palette
  1. Fill in the API URL in the next pop-up window. The API URL depends on your Make zone. For example, the US1 Make zone has the API URL: us1.make.com/api.

If the app, you want to access, originates from a different zone than your account, enter the app's zone to access its content.

Filling account's /app's zone
  1. Fill in the label for the environment in the next pop-up window. Press Enter to confirm the environment label.

Entering environment name
  1. Copy your Make API key in the last pop-up window. If you don't have an API key, follow the procedure to create a Make API key.

Entering Make API key

The Make Apps Editor extension restarts with the environment configuration.

A new sidebar appears in VS code with a list of your custom apps and Make open-source apps. If you previously created any, your custom apps are listed in the block My apps. The Make open-source apps are listed in the Open source apps field at the bottom of the VS code sidebar.

The open-source apps' code is only available in the EU1 zone.

If your zone is different and you want to access their code, create a new environment with eu1.make.com/api environment URL by following the steps starting from the 5th.

Switching among environments

In Make Apps Editor, you can work across multiple environments.

Easily identify the active environment by checking the indicator in the bottom status bar.

Upon clicking the environment indicator, you can switch among multiple environments.

Environment indicator
Switching among multiple environments

You can add another environment by issuing the >Add SDK environment again (follow the steps starting from the 5th).

Useful settings

You can add extra settings in Extensions > Make Apps Editor > Extension Settings > settings.json file.

Extension Settings in Make Apps Editor app

Here are some settings for better performance and experience:

  • Set editor.formatOnSave to true in VS Code settings. Source codes will be formatted automatically when you save them.

  • Set editor.quickSuggetions.strings to true in VS Code settings. Keyword recommendations will automatically show up while you're typing in IML strings too.

Log out and log in to an environment

You can log out using the >Logout command and log back in by the >Login command.

When you log out, the API key is removed from the settings.json file. To log in again, you will need to enter your API key.

You can use this flow to change your API key in an environment.

Manage testing and production app versions

The development of testing and production app versions is available in the VS Code extension with the Local Development for Apps feature. To learn about the current git versioning support, check the dedicated section.

This page describes how to develop an application with the Make Apps Editor, managing production and test versions of the application.

  • The developer writes and tests the code with the test application in Make.

  • The developer tracks changes in the local git repository, pulling changes from the test application.

  • The developer pushes the changes to the production application from the local testing app when they finish the development and testing.

This process improves the maintenance and stability of the application because the development does not influence the production version of the application. In addition, all changes can be tracked in a git repository, providing a clear and organized development workflow.

Prerequisites

To start the development of testing and production app versions, the following is needed:

  • The production version of an app in Make - If you already have an app that is already in use, use it as the production app.

  • Cloned production version to the local workspace - Clone the app to the local workspace, if you haven't done so yet, by following this manual.

  • The testing version of an app in Make - Create a new app in Make, with no content, that will function as the testing version of the app.

  • Optional, version control with Git - To properly track all changes in the local app, it is recommended to use a Git repository, for example, GitHub.

Development flow

Below is a diagram explaining how a developer can develop testing and production app versions.

App development flow diagram

Create a testing version of an app

First, you will need to create a testing version of Make app. Follow the instructions in the article below, to create a new origin to the Make testing app.

Once the origin for the testing app is successfully created, you will need to deploy the current code from the app, that already exists, which we can call "Production".

  1. Right-click on the makecomapp.json file and select Deploy to Make (beta).

Deploy the app to Make
  1. In the dialog, select the app origin that represents the testing app.

Selecting the Testing app origin
  1. The content of the local app will now be deployed to Make. Whenever a new component is about to be created, a dialog will prompt for confirmation. Press Enter to confirm the creation of the component. If you don't want to create the component, click Ignore permanently/do not map with remote option.

Confirming creation of new RPC
Confirming creation of new module
  1. The app has been deployed to Make.

Develop the components in the testing app

Now, you can start developing new components or editing the current components in the Testing app in Make, and thoroughly test the app in Scenario Builder.

Pull changes from the testing app to the local app

Once you finish the development of new changes and components in the testing app you can push the changes to the local app.

Deploy the changes from the local app to the production app

To synchronize changes made to the testing app with the production app, follow the steps outlined in the manual below.

Develop app in a local workspace (offline)

Unlike online development within Make Apps Editor or web code editor, local development doesn't provide access to advanced features such as prefilled code templates, IML object suggestions, and seamless integration with Scenario Builder for continuous testing.

Instead, it offers a self-contained environment for app creation and modification, ideal for situations where internet connectivity is unreliable or where comprehensive testing within Scenario Builder isn't required. Additionally, local development allows synchronization with Git repositories and provides full search capabilities across the entire codebase.

If you prefer app development in the App Editor, follow this manual, which describes online development, instead.

Develop a new or edit a current component in a local app

  1. If you don't have an app cloned yet, do so by following this manual:

  1. The structure of the app is very similar to the one in Make Apps Editor. Each group of components, such as RPCs, modules, or custom IML functions, contains the component directories with corresponding code files.

Make App Structure - Modules > module > Communication
Local App Structure - modules > module > communication

Each file within a local app component has a name in this format:

component's-name.app's-block.iml.json

for example:

execute-rule.communication.iml.json where execute-rule is the name of the module and communication is the name of the module's block.

  1. If you need to edit the current code, click the file you want to edit and develop your changes in the opened tab. Save the changes.

  2. If you need to create a new component, right-click the corresponding components' folder and select New Local Component: <component's name> (beta), for example, New Local Component: Module (beta).

Creation of a new component
  1. Follow the dialog and enter the component's name, label, and other corresponding parameters as you are used to from Make Apps Editor.

One of the dialog questions - Enter the Label of a new module
  1. Once all the questions are answered in the dialog, a new component with the corresponding files is created.

New module Create a House with all corresponding files
  1. Write the code in the corresponding files and save the changes.

Commit the changes in the Git repository

Deploy the changes from the local app to the Make app

The changes in components or new components can be partially deployed to Make, or the app as a whole can be deployed to Make.

Groups

If the app has over 10 modules, the modules should be put into special groups. These groups should be named after the entity with which the modules work, or after the type of the job, the modules are executing.

Example of groups - App CloudConvert
Example of groups - App SugarCRM

Also, the modules inside these groups should follow this order:

  1. Trigger module

  2. Create module

  3. Update module

  4. Get module

  5. Download/Upload Module

  6. List/Search module

  7. Delete module

The universal module should be left inside "Other" group. More about groups can be found here.

Scenario Debugger

While in Live Stream, you can't display historical logs in a scenario, in Scenario Debugger you can.

It displays the history of the scenario runs and enables you to search modules by their name or ID.

Scenario debugger

Searching modules by their name or description

To search the module by its name, enter the search term (name or module's ID) in the search field in the left panel of Integromat DevTool in the Scenario Debugger section.

Searching by module's name/description

Open settings of a module

Double-click the module's name to open its settings in the scenario editor.

Open a module's settings

View request or response details

View request details by clicking the desired operation.

Request/Response details

Generate your API key

  1. Navigate to your profile settings in Make.

  2. Click on the API tab.

  3. Click the Add token button.

4. Set API scopes for the API key.

The required scopes for the Make Apps Editor are:

  • sdk-apps:read

  • sdk-apps:write

Selecting scopes for API token

Add more API scopes to your API key if you want to use them. The description of the API scopes is documented in the .

5. Click the Save button to confirm selected permissions.

6. Copy your new API key to your clipboard and store it in a safe place. This is the only time when you can see the whole API key. The API key is required to set up the VS code extension.

You have created a new Make API key. All your Make API keys are listed in your Make profile in the API tab. In the API tab, you can view permissions for all your keys and delete unused Make API keys.

Live Stream

Live Stream displays what is happening in the background once you've hit the Run once button in your scenario. It allows you to view the following information for each module in your scenario:

  • Request Headers (API endpoint URL, HTTP method, time and date the request has been called at, request headers, and query string)

  • Request Body

  • Response Headers

  • Response Body

After you run a scenario, click one of the tabs in the right panel of Integromat DevTool to view the desired information.

Live Stream - detail of a log

Searching Requests and Responses

Enter the search term into the search field in the left panel of Integromat DevTool to only display requests that contain the search term.

Searching in logs

Removing requests from the list

To clear the list of requests recorded by Integromat DevTool, click the trash bin icon in the top-right corner of DevTool's left panel.

Bin button

Enabling console logging

To enable logging into the console, click the computer icon () in the top-right corner of the DevTool's left panel. Logging into the console is enabled when the color of the computer icon switches to green.

Console logging

Retrieving the request in the raw JSON format or cURL

To retrieve the raw JSON content of the request, click the Copy RAW icon () in the top-right corner of the DevTool's right panel.

Raw JSON

cURL can be retrieved using the Copy cURL button next to the Copy RAW button.

Create a new app origin

In the app development process, there are situations where it can be highly beneficial to push changes to multiple app origins. This is especially useful when managing different versions of an app, such as maintaining both a testing and a production app.

In this manual, the steps to create a new app origin for a testing app version are described.

  1. If you haven't cloned the production app to a local workspace, do so by following the manual.

  2. If you haven't created a testing app in Make, do so by creating a new app in Make and naming it <App Label> Testing so it is obvious it is the Testing version.

  3. Go to the makecomapp.json file in the local app repository. Locate the origins array of the collection and enter a new item by copying the code below and editing the values as instructed under the code.

If the local app is shared among multiple developers, the originsarray will contain records for each connection between their local and Make (remote) app. Do not edit the current records to prevent breaking the connections.

Creating a new origin for testing app
"origins": [
    { /* --- Existing origin --- */ },
    {
        "label": "Testing",
        "baseUrl": "https://eu1.make.com/api",
        "appId": "my-first-app-test",
        "appVersion": 1,
        "apikeyFile": "../.secrets/apikey"
    }
]
  • label - the label of the local origin

  • baseUrl - the URL to the origin's zone

  • appId - the name of the app

  1. Save the changes in the makecomapp.json file.

Approved apps

This section explains the work with updates in an approved app.

Changes

Once an app is approved by Make, the code is locked and it starts to be versioned. When a new change is made in the code of the app, it automatically creates Diff files, which contain detailed information about the changes. Every change made to the app is visible only to you unless we commit it. You can safely add and test new functions and when they are stable, you must follow our guidelines to have the changes checked and released to users. The Diff files are available in both environments, the web interface, and VS Code extension. Also, you should always make sure the changes will not break existing scenarios.

The changes you make will become available only after triggering your scenario in your web browser. This can be done by clicking the Run once button, or by selecting the Run this module only option after right-clicking on a module with your mouse.

To make the changes available to all users in Make, you must request approval of the changes. Once approved, the changes will be available to all users. Additionally, you will have the ability to schedule your scenario with the updated modules, that were previously run in "run-once" mode.

Run once button
Run this module only option

Versioning

When deciding whether to update the current app or create a new app version, take into account the user experience. Updating tens or hundreds of scenarios might be complicated and a time-consuming process.

If possible, you should prioritize updating the current app instead of creating a new version.

It is recommended to create a new version of the app, when there are major changes in the current API and/or there is a new API version available, and it is not possible to update the current app without breaking changes.

This way, you will make sure that everything is consistent, there are no breaking changes made in the current app, and at the same time, users will be able to upgrade the modules in their scenarios using our upgrade module tool. Read more about the upgrade module tool in our Make Help Docs:

Upgrade module dialog

Creation of a new version of an App

If you need to create a new version of your app, create a new app, and develop its content. Once your app is finished, request for app's review. During the app's review, do not forget to mention the app should be compiled as a new version of the current app.

Pull changes from Make app

Once you finish the development of new changes and components in the Make app you can push the changes to the local app. To do so, follow the steps below.

  1. Go to Visual Studio Code and open the local directory with the local app file.

If you are uncertain how to do so, simply follow the steps in the corresponding section in the article.

  1. Locate the makecomapp.json file and right-click it. In the pop-up menu select Pull All Components from Make (beta) option.

  1. In the dialog, select the testing app origin.

  1. All changes in the existing components will be pulled into the local components. If there is a new component, a dialog will appear. Either confirm the creation of the component by pressing Enter or click Ignore permanently/do not map with the remote option.

  1. To properly track the new version of your local app in a local workspace or git repository, follow the corresponding steps in this article.

Types of RPCs

RPCs can be used in multiple ways. The number is restricted only by your imagination. In this chapter, we described the most common ways of RPC usage.

Options RPC

The most common use is the replacing of a select parameter with static options to select parameter dynamic options. Thanks to RPCs, we are able to retrieve a list of all available options right inside the parameter, according to the user's perspective.

Fields RPC

There are web services, which allow users to have their own structure of data. Therefore, it is needed to make the mappable parameters semi-dynamic, e. g. support custom or dynamic fields.

Samples RPC

The purpose of this RPC is to retrieve sample data dynamically for a module. Replaces hard-coded samples, which might become outdated quickly.

Components

RPC consist of 2 components: Communication and Parameters.

Communication

Communication can be request-less.

Same as the modules, you can use pagination in RPCs to iterate over the records.

Parameters

Parameters from the modules are passed automatically to linked RPCs.

Updating your app

During maintenance of your app, you might need to apply changes or create a new app's version.

The process is different between private/public apps and approved apps.

Changes and versioning in custom apps

All changes take immediate effect in running scenarios. You should take this into account while changing your code!

Changes and versioning in approved apps

Since your app has been approved, every change made to the app is visible only to you unless we commit it. You can safely add and test new functions and when they are stable, you must contact us to check and release them for users.

JWT

This section describes how to generate a JWT in Make

There's no dedicated JWT connection type because the JWT itself is only a "special format" of the Authorization header. Everything works the same way as described in Basic Connection chapter.

The options argument refers to the same options object as in the npm library.

Generating a JWT

To generate the token, you can use the following example.

{
  "url": "https://mock.api/connect",
  "temp": {
    "jwt": {
      "iss": "https://iam.the.issu.er",
      "iat": "{{timestamp}}",
      "exp": "{{timestamp + 30000}}",
      "scope": ["rest_webservices"],
      "aud": "https://iam.the.audien.ce/services/rest/auth/oauth2/v1/token"
      },
    "options": {
      "header": {
        "kid": "{{parameters.clientId}}"
	}
      }
    },
    "headers": {
      "authorization": "Bearer {{jwt(temp.jwt, parameters.clientSecret, 'HS512', temp.options)}}"
    }
}

As you can see in the example, first, we build a JWT payload inside the temp variable called jwt, but you can use any other name for that variable.

Then, inside the Authorization header, we call the IML function named jwt. The jwt function accepts four parameters:

  1. The payload to be signed.

  2. The secret to signing the payload.

  3. The algorithm. The supported algorithms are HS256, HS384, HS512, and RS256. The default value is HS256. This parameter is optional.

  4. A custom header to customize the JWT authorization header. This parameter is optional.

This function will output a JWT token which you can use in the Authorization header.

App review

When you create a custom app you can use it anytime. When you finish the development and testing of your app, you might share the app with selected users with an invite link. But if you want to provide your custom app to all Make users, we at Make want to make sure that your app is up to Make apps standard. To check that, Make has the app review process.

App review prerequisites

If you want to provide your app to other Make users, you have to request a review of your app first. Check the App review prerequisites for rules and requirements for the App's code.

When you request an app review, the Make QA team will check your custom app's code. If your app meets Make's requirements and follows the app development best practices, Make will publish your app and make the app available to all Make users. Otherwise, the Make QA team will contact you with instructions how you should improve your custom app.

Request app review

After you check the app review prerequisites, you can request an app review. Click the link below for instructions on how to request an app review.

Check the review status

The review process consists of multiple stages. You can check the current stage of the review in the Flow tab.

Approved app

When Make approves your app, the app becomes available to all Make users. Because of that, your custom app management changes. Read the linked section to find out more.

Maintenance of approved app

It is important to keep your app up-to-date and provide support to your app's users. More information can be found below.

Responder

The Responder module is used for sending response to the sender of a web hook.

The Responder should be used when you need to send processed data back to the service. The scenario gets initiated by an Instant Trigger, does its' stuff with the data received, and then sends the results back to the sender. The responder module has no interface, you just pass parameters in.

Components

Communication

  • Only a response directive is available inside the communication.

Static Parameters

You can use static parameters inside the Responder module without any restrictions.

Mappable Parameters

You can use mappable parameters inside the Responder module without any restrictions.

Example

Example of Use of Responder Module
{
	"response": {
		"body": {
			"text": "{{parameters.text}}"
		},
		"status": 200,
		"headers": {
			"content-type": "application/json"
		}
	}
}
[]
[
	{
		"name": "text",
		"type": "text",
		"label": "Message"
	}
]

Not attached

The new URL address, which has been created, has to be then registered manually by the user. Basically, the user copies the URL address and pastes it to the webhook's settings of the web service.

The added value of not attached webhook is the existence of an interface and the fact, that the user is notified there is an instant trigger available.

If your app will be approved by Make, there will be a manual on how to register the webhook available in the app's docs.

Unless there is a reason (read below), the connection should not be connected to the webhook.

Examples of reasons, why not attached webhooks should have a connection connected:

  • an RPC is used in parameters in the webhook

  • an RPC is used in the instant trigger

  • an additional call is executed in the instant trigger (for example a call that retrieves data for the record ID)

Webhook URL Available to Copy

Dedicated

Unlike shared webhook, dedicated webhook is directly linked to the user account. Only notifications for the specific user are received.

Even when the service sends all data to only one URL registered for the user, it's dedicated webhook. It's up to you how the app will handle incoming data. Over 90% of services use dedicated webhooks, think twice before using shared one.

Types of dedicated webhooks

Attached dedicated webhook

The new URL address, which has been created, is automatically registered to the service using attach procedure, and can be unregistered using detach procedure.

If there is an endpoint for registration of webhook in the API available, the attach directive should be implemented.

Not attached dedicated webhook

The new URL address, which has been created, has to be then registered manually by the user. Basically, user copies the URL address and pastes it to the webhook's settings of the web service.

If there is no attach directive available in the API, but the web service allows setting up a webhook manually, implement the dedicated webhook without attach directive.

Set the app's icon in VS Code

Make sure your app icon meets the requirements outlined in the article below.

Set or change the icon

To view, set, or change the icon of the app, click the right mouse button on the app name and choose the Edit icon option.

Edit icon option on the list

A new view will appear and you will see the preview of the current app's icon inside the app module.

Current app's logo and color

You can change the icon by clicking the Change icon button. The file upload dialog will appear and after you confirm the chosen icon, it will be uploaded back to Make. The change icon view will close and the new icon will appear in the left tree.

App's logo in the left tree
New app's logo and color

Use general controls

Edit the source code

To start editing the source code, find the item you want to edit in the left menu and click it. A new editor will appear and the current source will be downloaded from Make. You can edit it as a normal file. If your app contains some RPCs or IML functions, they will be provided to you.

After pressing the shortcut CTRL+S, the source code will be automatically uploaded back to Make.

Add a new item

To add a new item, such as a module, connection, webhook, etc., right-click the corresponding folder and click the New <item> option.

Each time the prompt will appear, you will be asked to fill in information about the newly created item. Just go through it. Your new items will always appear under the corresponding folder.

Edit metadata

To edit metadata (for example to change a label of a module), right-click the desired item and select the Edit metadata option from the menu.

The prompt will appear allowing you to change allowed values. If you don't want to change a value, skip the field by pressing the Enter key.

Change the connection or webhook

To change the attached connection or webhook of an item, right-click the item and select the Change connection (or webhook) option from the menu.

The prompt will appear allowing you to change the connection or webhook. If possible, there will also be an option to unassign the current connection without assigning a new one.

Also, the prompt to assign an alternative (secondary) connection will appear. Please note, that the alternative connection should not be the same as the main connection. You can leave it empty.

Delete an item

To delete an item, right-click it and choose the Delete option.

You will be asked to confirm the deletion. If you answer Yes, the item will be deleted from the app.

Deleting items is only possible in private apps. Once an app is published, the capability to delete items within the app is disabled. For further details regarding apps' visibility and the deletion of items, please refer to .

Generate the interface code

Utilize the Interface Generator to generate an interface. Familiarize yourself with the Generator's functionality by following the steps described in the Interface Generator article below.

Debug IML in Web Browser

1. Output a message to the web console

You can use debug inside your IML functions to print a message or mid-results of your code. During the function execution debug messages will be visible inside the console of your browser.

To open the developer console in Google Chrome, open the Chrome Menu in the upper-right-hand corner of the browser window and select More Tools > Developer Tools. You can also use Option + ⌘ + J (on macOS), or Shift + CTRL + J (on Windows/Linux).

By using debug() you can understand what data you are manipulating inside a function.

2. Debug JavaScript snippet in Chrome DevTool

To open the developer console in Google Chrome, open the Chrome Menu in the upper-right-hand corner of the browser window and select More Tools > Developer Tools. You can also use Option + ⌘ + J (on macOS), or Shift + CTRL + J (on Windows/Linux).

  1. Open Developer Tools in Google Chrome.

  2. Go to Sources -> Snippets. Create a new snippet.

  3. Paste your code. Before execution you will need 3 more things:

    1. Input example. You can either type it manually or use debug() inside the IML function on Make and copy input from the developer console of your browser (more about debug() ).

    2. Call function. Since we are testing the function locally, we cannot run a scenario to execute it. To imitate scenario execution we need to call a function by its name and send the input, that we specified before, inside it. Example: myFunction(input) .

    3. Remove all IML functions called inside. All debug() and iml.function() should be removed or replaced by similar JavaScript functions.

  4. Press ⌘ + Enter (on macOS), or Ctrl + Enter (on Windows/Linux).

  5. Click on a line number to put a breakpoint and start debugging! In Scope, you will see variables that you are using at a particular moment. In Console, you will see all your console.log() messages.

  6. If you're interested to explore Chrome DevTools check their docs .

Create your app

When you want to create a custom app, you have to set up the custom app metadata, such as:

  • a unique name of the custom app

  • custom app label that Make displays in the scenario editor

  • color of the app's modules

  • app icon

This page will guide you through these settings.

  1. Navigate to Custom apps in the left sidebar menu in your Make account. You might have to click on the three dots at the bottom of the left sidebar to view the Custom apps option.

  2. You will see a list of sample custom apps. Click Create a new app in the top right corner.

  3. In the Create an app pop-up, fill in:

  • Name: The unique identifier of your custom app. Make uses the custom app name internally. Note the requirements under the text box.

  • Label: The custom app label. Make will display the custom app label in the scenario editor. You can use any characters in the custom app label.

  • Description: Description of your custom app. This field is optional.

  • Theme: The color that Make uses for your custom app modules and forms. Use the .

  • Language: The language of your custom app and its descriptions.

The language setting in the custom app is informative. Make doesn't translate the custom app descriptions.

  • Audience: This parameter has no effect currently. The custom app is accessible regardless of the user's country.

  • App icon: Upload icon. The icon upload is optional. Make will use this icon to create your custom app logo. The icon has to:

    • be a file in .png format

    • have 512 x 512 px dimensions

    • have a maximum of 512 kB size

Make processes the icon file so that:

  • All color pixels in the icon file are converted to white

  • All transparent pixels in the icon file are converted to your theme color

For detailed guidelines on how to create the app logo check the .

If you want to update the custom app icon, navigate to your custom app settings and click Options > Edit.

  1. Click Save changes to confirm the custom app settings.

You created a custom app in Make. The next pages will guide you through setting up the app's base, connection, error handling, and modules.

Error handling

The best way to deal with errors is to use an error handler. An error handler connects to a module with the error handling route. When the module outputs an error, the error handling route activates and runs the error handler.

When all errors are handled, Make keeps scheduling scenario runs instead of disabling the scenario.

Read more about error handling in Make:

Setting up an error handling for our demo API

When you were trying your new search module, you were probably experiencing error 401, but you didn't know why was that, if you didn't check Integromat DevTool. Since Make offers advanced error handling, you should set your app the way, so you could understand what is wrong with your module/scenario right away and use error handlers.

Enter your search module into your scenario and create a new connection with a random API key, if you haven't done so before. You should see the error:

In DevTool, you should see this output:

We need to make sure the error from the body of the response is returned in the module's output as well.

As you learned before, error handling should take part in base, since it is an element, which is shared among all modules and remote procedures. Therefore, open base and replace the current code with the code below:

Notice, that a new element response with error handling was added.

Basically, we mapped the error parameter from the response body. So now, when an error is returned from the API, the error message contains the statusCode together with error wording.

Now, go to your scenario again, and execute the search module. You should now see the detailed wording of the error:

Notice, that the wording of the error is now available. Thanks to this, you know where is an error and how to solve/handle it.

Voila! You just learned all the basics of custom app development in Make!

If you want to continue in development of your custom apps, you can explore our docs and learn more!

Trigger modules

Epoch in polling trigger

The trigger module has an Epoch section that defines what "Choose where to start" looks like. This section is an RPC that uses everything in COMMUNICATION including the pagination. This means there can be an issue if the user has too many objects which could be returned by this RPC so that is why a "limit" parameter should be specified here.

‌ The "limit" parameter should be a static number which should be at max 300 or 3 * number of objects per page.

API endpoint requires from and/or to date parameters

Some API services require date parameters that define the interval of records to be retrieved, e.g. from and to, fromDate and toDate, etc.

In this case, it is important to handle the date parameters correctly.

Notice the From and To parameters that are required.

Since triggers don't allow mapping/functions, the user has to "hardcode" the From and To dates. Therefore, Make will always request the same interval of records.

Notice there is no From or To available to the user.

Notice the mapped data.lastDate that is available in polling triggers.

This variable is available to the user in Choose where to start setting.

The behavior of the supported options:

  • From now on - The current date will be sent.

  • Since specific date - The date provided will be sent.

  • Choose manually - The date of the chosen item will be sent.

  • All - The default date 1970-01-01 will be sent.

Debugging of pagination in list/search modules

If API supports pagination, it should be implemented. In order to make sure the pagination works as intended, it is recommended to set the page size to a low number, if possible, see the example below:

Then, it is needed to create as many records as one page and a few more. You can do it by using Flow Control > Repeater module, which also returns the ID of the repeat. Map this ID in the following module as it will help to differentiate the records.

Once the testing records are created, you can test your search module. Thanks to using the ID from the repeater, you are able to see whether the records are ordered and how, and whether the records are correctly retrieved, e. g. they are not being duplicated (1 page retrieved multiple times).

In the console, you can also effectively control every retrieved page and its size.

Possible pagination issues:

  • The stop condition limit set by the user doesn't work. Therefore all the records that exist in the account are retrieved (or only the first page if there is also the issue from the point below).

  • The next page condition isn't set correctly so the next page isn't retrieved even though it should according to the user's limit and the content of the account.

  • The next page condition isn't set correctly so the next pages are retrieved even though all records were already retrieved. They can either be duplicated or without any records (without looking into the console, you will not spot them).

  • The pagination is not optimized so even though there is a parameter saying “there is no other page to retrieve”, it still retrieves the next page which is empty (developer implements the pagination for offset even though he/she could implement it using the cursor parameter).

  • The value in the parameter page is too low therefore there are too many pages being retrieved (= too many calls).

  • Not common - the records are duplicated because the pages are overlapped (1st page 1-100, 2nd page 100-199 instead of 1-100, 101-200).

Processing of output parameters

How to process the API response

The best approach is to return the API response as it is. In many cases, the response varies based on the user who is using the app, as responses can contain different custom fields, etc. If the response returned is unchanged, and if still all the parameters aren't described in the output parameters, Make will automatically learn additional parameters from actual incoming data and propose them for mapping.

Response output

A module's response output should be defined for the case when a request is fulfilled successfully. The output definition should under no conditions contain error messages (this belongs in the Base section's error handling) nor the additional metadata which may arrive bundled with the actual response information.

No matter the module type, the output shouldn't be defined like this:

Showing dates correctly

Requires IML functions enabled.

There are multiple ways how a date can be returned from the service, either as a timestamp or in any format the service finds fit. It is important to parse this date to our ISO 8601 format so it is shown in an output of the module as a date using the users' localization and timezone.

Make is using ISO 8601 format: YYYY-MM-DDTHH:mm:ss.sssZ

Any other format, even just without milliseconds won't be shown correctly in the output and needs to be parsed using an IML function.

Interface

The interface describes the structure of output bundles and specifies the parameters which are seen in the next modules. It should contain the full response from the API, including nested parameters.

You can generate an interface using our Interface Generator, learn how in section.

App logo

Every app on Make has its own logo and color. The combination of logo and color represents the app. With a distinct logo and color, the users quickly see which module belongs to which app.

App theme

The app theme is the module color in hexadecimal format. For example, the Make module's color is #6e21cc.

App logo

To add a logo to your custom app, you need to make sure that your file with the logo meets these requirements:

  • an image file in .png format

  • square dimensions: minimum size 512 x 512 px and maximum size 2048 x 2048 px

  • a maximum size of 512 kB

Make processes the logo file so that:

  • Areas in the logo that are white or transparent will be displayed as the color specified in the Theme field.

  • Areas in the logo that are black will be converted to full opacity and will be displayed as white.

  • Areas in the logo that are in color or are semi-transparent will be displayed as the color between white and the color specified in the Theme field.

If you don't have any tool to edit your file with the logo, you can use the free editor.

For creating a transparent layer, you can use the tool that is available in Lunapic.

Examples of how logos are rendered

Work with semi-transparent pixels

An app can have only one theme color. You can use multiple transparency levels to give your logo multiple shades of the theme color. You can create a 3D effect too.

Updating the app's theme and/or logo

You can update the custom app's logo anytime. Navigate to your custom app settings and click Options > Edit.

If your app has been approved, the change of theme/logo is a subject of a change that needs to be approved by Make.

Logo changes not visible?

After you upload or update the logo or theme color, it may take up to 1 hour for the changes to be propagated across the Make platform.

Approved app

When Make approves your app, your app becomes available to all Make users in their scenarios. To the users, an app developed Make looks the same as the app developed by you. You have to take your custom app's management responsibly, since any Make user might be using your custom app in their scenario.

After a custom app's publishing, Make provides you with a development version of your custom app. You can find the development version of your app by searching for your app in the scenario editor:

The search now has two results:

  • The app with the tag custom app: This is a development version of your app that only you can see and work with. When you update your custom app, the development version of your app will have your changes.

  • The app without a tag: This is a public version of your app. Anyone can use this version of the app. Changes to this version have to be approved by Make.

If you need to make changes to the app's public version, you have to test your changes in the version labeled custom app first.

When you are done with the updates and testing check the link below for the next steps.

It is important to keep your app up-to-date and react to API changes on time, e.g. when the API is going to shut down. Also, it is needed to fix issues reported by users.

Read more about maintenance of approved apps below.

Base URL

Base URL is the main URL to a web service, which should be used for every module and remote procedure in an app, e.g. https://mywebservice.com/api/v1.

There might be situations, when you need to have a variable base URL, e.g. if your web service, which you are integrating, uses multiple domains, and you want to let your users to have access to the one they use.

Example 1

Here, is an example of how to handle 2 types of accounts - sandbox and production.

  1. First, add a checkbox in your connection parameters, which can be checked when the condition is met.

2. Then, both in the connection and the base, there should be a condition implemented:

3. All modules and remote procedures then can use hard-coded "url": "/uniqueEndpoint".

Example 2

Here, is an example of how to handle 2 types of accounts - eu and us.

  1. First, you need to set up select in your connection parameters, where you let your users choose from available environments:

2. Then, both in the connection and the base, there should be the environment mapped:

3. All modules and remote procedures then can use hard-coded "url": "/uniqueEndpoint".

Sanitization

Sanitization will help you to protect sensitive data (passwords, secret keys, etc.) from leakage.

You should always the log, so no personal tokens and/or keys can leak.

If you don't use sanitization at all, the request and response logs will not be available in the .

Accesstoken is correctly mapped, therefore it is not exposed.

Without sanitization, there will be no log in the list of executions in Live Stream.

At the same time, there will be no shown log in Scenario Debugger, as in the screenshot above.

Either developer or user can't see the original request and response, and debug possible issues.

Notice, the accesstoken was misplaced for token. Therefore the accesstoken was exposed in the log.

Even though there are many standardized authorization protocols (like OAuth 2), there are many services thinking they can do it better so remember to check how the service implements the authorization and set it up correctly.

Remote Procedure Calls

The Remote Procedure Call, shortly RPC, is a function call, which executes a call to fetch additional data inside a module. A user cannot select it or invoke it from other modules.

RPCs have specific output rules, so take a look at before the implementation.

Limits

As we can't wait for the RPC's output to infinity, there are limits.

Name
Total limit of...
Value

Best Practices

Since we can't wait forever for the RPC's response, when the parameters of the module are loading, there are some best practices you should know.

Name
Recommended limit of ...
Value
Dynamic Options RPC
Dynamic Fields RPC
Dynamic Sample RPC
Communication
Parameters
Private/Public apps
Approved apps
Basic connection
App review prerequisites
Request app review
Review status
Approved app
Terms of approved app maintenance
Attached
Not attached
[
   {
      "name": "sandbox",
      "type": "boolean",
      "label": "Sandbox"
   },
   ...
]
{
    "baseUrl": "https://{{if(connection.sandbox,'sandbox.', '')}}yourapi.com/api"
}
[
    {
        "name": "environment",
        "type": "select",
        "label": "Environment",
        "options": [
            {
                "label": "EU",
                "value": "eu"
            },
            {
                "label": "US",
                "value": "us"
            }
        ],
        "default": "production"
    },
    ...
]
{
    "baseUrl": "https://{{connection.environment}}.yourapi.com",
    ...
}

Max Execution Timeout

... seconds

40

Request Count

... calls performed by RPC

3

Record Count

... paginated records

3 * number of objects per page

RPC Types
hexadecimal color code
dedicated page
A Custom App Dialog
...
"qs": {
         "from": "{{data.lastDate}}",
         "to": "{{now}}"
      },
...
IML variable
No "limit" parameter
From and To parameters required from the user
Start Date and End Date are not available to user
Choose where to start feature
{
    "url": "/contacts/filters/{{parameters.filter_id}}",
    "method": "GET",
    "qs": {
        //"per_page": 100
	"per_page": 10 //set value for testing
    },
    "response": {
        "output": "{{item.contact}}",
        "iterate": "{{body.data.contacts}}",
        "limit": "{{parameters.limit}}"
    },
    "pagination": {
        "qs": {
            "page": "{{pagination.page}}"
        },
        "condition": "{{body.data.max_page > body.data.page}}"
    }
}
Creation of Testing Data Using Repeater
Example of Array Output
Example of Paginated Response
Lunapic
Transparent Background
An example scenario with purple Make, green Google Sheets, and blue Microsoft 365 Email modules.
Example of logos with one color
Example of grayscale logos
Example of color logos
Example apps with multi transparency levels
...
"log": {
        "sanitize": ["request.headers.token"]
    }
...
sanitize
console
...
"log": {
        "sanitize": ["request.headers.accesstoken"]
    }
...
Example of log from console with sanitized access token
Output from Scenario Debugger
Example of log from console with exposed access token
Create a new app origin
Pull changes from Make app
Deploy changes from local app to Make app
Clone Make app to local workspace
Commit the changes in Git repository
Deploy changes from local app to Make app
Communication
Static parameters
Mappable parameters
App logo

Names, labels & descriptions

Module names

A name of a module, an RPC, or a custom IML function should not match with any reserved word in JavaScript. See the list of reserved words in JavaScript here.

Module labels

Every module should have a label that precisely describes the module's use. For each type of module, there is a standard naming convention. But it may change depending on the functionality of the module. The label should be composed of the verb expressing the intended action (Create, Update, Watch, etc.) and the name of the entity being processed (Customer, Invoice, Table, etc.).

Names of modules follow sentence case. Only the first word is capitalized.

  • Watch events

  • Create a report

  • Update a record

  • List photos

  • Search files

  • Add members to a group

  • Get a group's info

  • Watch Events

  • Create a Report

  • Make a Call

  • List Photos

  • Search Files

  • Add Members to Group

  • Get Group Info

Triggers and instant triggers (webhooks)

These modules watch for new data in a given service and return it. Compose the label according to this pattern Watch <watched node>

Examples:

  • Watch events

  • Watch photos

  • Watch deleted files

Actions

These modules write data into a service, modify data in the service, or retrieve a single result. Compose the label using simple verbs like Create, Get, Update and Delete and the modified or created node. Use the naming convention of the service you are implementing.

Examples:

  • Create a note

  • Update a file

  • Get a user

  • Delete a task

Searches

These modules retrieve data from the service and allow retrieving one or more results. Compose the label using simple verbs like Search or List. Use the naming convention of the service you are implementing.

Examples:

  • Search files

  • List tasks

Module descriptions

In a few words, describe the functionality of the module. Write the description in the third person and capitalize only the first letter of the first word in the description (like a normal sentence structure).

Example: Description for the module Update a time entry:

Updates a time entry for a specific user.

Update a Time Entry for a Specific User.

The description is missing "s" for word "Update". The "Time Entry" and "Specific User" should be lowercase.

Triggers use the "Triggers when..." in the descriptions. For example, the description for the module Watch new users should be: **"**Triggers when a new user is created."

Names and labels of input and output parameters

For labels, try to use the same names and conventions as in the integrated service. It helps users to use the app in Make and not be confused.

For variable names, use the same names that come from the service API. It helps when debugging for both advanced Make users and support agents. Ideally, the output of the module should be the same as the response from the API.

Example:

[
    {
        "name": "id",
        "label": "ID",
        "type": "text"
    },
    {
        "name": "note",
        "label": "Note",
        "type": "text"
    },
    {
        "name": "firstName",
        "label": "First Name",
        "type": "text"
    },
    {
        "name": "shortUrl",
        "label": "Short URL",
        "type": "url"
    }
]
{
    "id": "59b1396a4a22a7a3bfc78e22",
    "note": "Hey",
    "firstName": "John",
    "shortUrl": "https://trello.com/c/LdcrM4wa"
}

Abbreviations

Be sure to use the correct uppercase for abbreviated words, such as ID, IDs, URL, GPS, VAT, etc.

Attached

The new URL address, which has been created, is automatically registered to the service using attach procedure, and can be unregistered using detach procedure.

A connection should be attached to the webhook.

Attach

The Attach remote procedure is used to automatically attach a webhook to a remote service. Please note that you will need to detach this RPC later, and for that you will need this remote procedure’s Id.

{
    "url": "https://www.example.com/api/webhook",
    "method": "POST",
    "body": {
        "url": "{{webhook.url}}"
    },
    "response": {
        "data": {
            "externalHookId": "{{body.id}}",
            "token": "{{body.token}}"
        }
    }
}

To save the remote webhook id (in order for detach to work), you must use the response.data collection. This collection will be available in the detach webhook remote procedure as webhook IML variable for you to use.

If the API returns a parameter, that should then be used in the detach remote procedure, you need to make sure the parameter is mapped in the response.data collection, otherwise the parameter will not be available via webhook.parameter mapping (see the token in the example above).

The webhook collection with webhook’s data is also accessible in regular remote procedure calls if the call is processed in the context of an Instant Trigger. For example, if you create a dynamic interface for an Instant Trigger based on parameters entered when the webhook was created.

Detach

The Detach remote procedure is used to automatically detach (delete) a webhook from a remote service when it is no longer needed. The only thing you have to do is to correctly specify the url to detach a webhook. No response processing is needed.

{
    "url": "https://www.example.com/api/webhook/{{webhook.externalHookId}}",
    "method": "DELETE"
}

Please refer to RPCs docs to get more information about writing RPCs.

this article
Adding a new function from list of available functions
Adding a new module
A new module
Editing metadata of a module
Change connection or webhook option
Changing a primary connection
Changing a secondary connection
Delete option
Deletion dialog
Interface
Public and development version of a custom app
Updating your app
Terms of approved app maintenance
function removeType(input) {
	debug(input)
	delete input.type; //removes type
	input['year'] = iml.parseNumber(input.year); //parses number from text
	return input;
}
here
here
During the execution of a module, IML functions called inside this module also will be executed. All debug messages that you specified in the IML function will be available inside the developer console of your browser.
{
	"baseUrl": "http://demo-api.integrokit.com/api/v1",
	"headers": {
		"api-token": "{{connection.apiKey}}"
		},
	"response": {
		 "error": {
            	     "message": "[{{statusCode}}] {{body.error}}"
        }},
	"log": {
		"sanitize": ["request.headers.api-token"]
	}
}
Introduction to errors and warnings
Quick error handling reference guide
Error 401 Before Implementing Error Handling
Error in the DevTool
Error 401 with Detailed Wording after Implementing Error Handling
https://github.com/integromat/make-apps-sdk-docs/blob/master/best-practices/broken-reference/README.md
https://github.com/integromat/make-apps-sdk-docs/blob/master/best-practices/broken-reference/README.md
Interface

Mappable parameters

Order of the mappable Parameters

The mappable parameters should follow this priority order:

  1. required parameters

  2. optional parameters

  3. advanced parameters

Advanced parameters should never be required.

Example of Wrong Mappable Parameters
Example of Correct Mappable Parameters

When defining the input parameters, try to use the same order as in the integrated service. Place required parameters at the top if it is possible. Position parameters in logical groups.

[
    {
        "name": "firstName"
        "type": "text",
        "label": "First Name"
    },
    {
        "name": "lastName"
        "type": "text",
        "label": "Last Name"
    },
    {
        "name": "email"
        "type": "email",
        "label": "Email"
    },
    {
        "name": "taskId"
        "type": "number",
        "label": "Task ID"
    }
]
[
    {
        "name": "firstName"
        "type": "text"
    },
    {
        "name": "taskId"
        "type": "number"
    },
    {
        "name": "lastName"
        "type": "text"
    },
    {
        "name": "email"
        "type": "email"
    }
]

Helps under parameters

Using a help directive, you may specify a hint of what is expected for the parameter when it is not that obvious from the label or the expected value is more complicated. The text should start with a capital letter and end with a period. Supports Markdown, such as _italic_, **bold**, `monospace`, , or URL [example](http://example.com).

Example:

{
    "value": "object",
    "label": "Collection or JSON",
    "nested": [
        {
            "name": "metadata",
            "label": "Metadata",
            "help": "Map a Collection or enter JSON in format `{\"key\": \"value\"}` where value can be any type from [Intercom Event Metadata types](https://developers.intercom.com/intercom-api-reference/v1.0/reference#event-metadata-types).\nFor example:\n`{\"order_name\": \"Order123\",\"price\": {\"currency\": \"eur\", \"amount\": 12345}}`\nMaximum 5 entries.",
            "type": "any"
        }
    ]
}
Example of a Help

Advanced parameters

Services like CRMs use large amounts of input parameters. In these cases, you can mark less important parameters as advanced. When the parameter is marked as advanced, then by default it isn't shown in the GUI. It can be found in the advanced parameters instead after the user clicks on Show advanced settings.

Example:

{
    "name": "externalId",
    "label": "External ID",
    "type": "text",
    "advanced": true
}

Action modules

Responsiveness approaches

Bear in mind that there are two approaches to responsiveness in a service.

  • Synchronous - The service upon an action request returns a result, which can then be processed in the following modules in a scenario.

  • Asynchronous - The service doesn't return anything at all, or doesn't return useful output, e. g. a processed file.

Comparison of synchronous and asynchronous approaches

Attribute/ Approach
Synchronous
Asynchronous

Advantage

The result is returned right away. Ability to proceed the result to the following modules.

Comes in handy when we need to process a large amount of data, e. g. file conversion.

Disadvantage

Regarding the type of job and its severity, the job can take too long. This might cause a timeout (default 40 sec). E. g. file conversion. The default timeout can be prolonged depending on the valid cases.

The scenario is not fluent. It is needed to create at least 2 scenarios - one for triggering the job, and another one for proceeding with the result from the first scenario. The second scenario, if possible, should start with an instant trigger that triggers once the job finishes.

Module Example

Example Scenarios

The scenario contains module, which has the synchronous logic implemented on app's side.

The first scenario contains module which has asynchronous approach by default. The only result is the job's ID.

Handling of asynchronous approach

When a web service doesn't support a synchronous approach and the common use case of the module requires support, it should be added on the app's side. Basically, there should be two (or more calls) executed instead of only one:

  • create a job call - a call that requests for the job, e. g. conversion of a file, file upload,

  • periodically check the status of the job - execute repeated calls to obtain the job's status**,**

  • ask for the result of the job - once the status call returns the awaited status, request the result, e. g. result file.

Example

After importing a JSON file to a web service, it requires a certain period of time to process the file. In this case, we have to keep checking if the status of the entity changed from processing to completed. In this case, when the status is completed, the result is already part of the response.

When the repeat directive is used, the condition and limit should always be provided to prevent infinite loops. Learn more about the repeat directive.

[
	{
		"url": "/v3/activities/contacts_json_import",
		"method": "POST",
		"body": "{{encodeParameters(parameters)}}",
		"response": {
			"temp": "{{parseResponse(body)}}"
		}
	},
	{
		"url": "{{temp._links.self.href}}",
		"method": "GET",
		"repeat": {
			"condition": "{{body.state != 'completed'}}",
			"delay": 1000,
			"limit": 300
		},
		"response": {
			"temp": "{{body}}"
		}
	},
	{
		"response": {
			"valid": "{{length(temp.activity_errors) = 0}}",
			"error": {
				"message": "{{join(temp.activity_errors, '\n')}}"
			},
			"output": "{{parseResponse(temp)}}"
		}
	}
]

App review prerequisites

When you want to make your custom app public and share it with all Make users, your app has to conform to Make standards. Following Make app development standards is a prerequisite to get an app review. Make app standards encompass:

  • Custom app functionality.

  • Custom app code best practices.

  • Testing your custom app with test scenarios.

Check out the following sections to learn more about each prerequisite.

Check your app's functionality

In Make, we develop apps to provide value to our users. Your custom app should use a service that Make doesn't integrate yet. All apps in Make require from the user only credentials that are necessary to create a connection to the service API. If you want to make your custom app public, your app functionality has to follow the same principles:

  • Your custom app uses a web service that is not already available in Make.

  • Your custom app and it's modules have to connect to a web service API. Avoid duplicating the same functionality as iterators, aggregators or other tools in Make.

  • Avoid using APIs that have strong dependencies on other APIs, or APIs that function as redirects to other APIs.

  • Avoid using APIs that don't have their own domain, or have their domain associated with service platforms like Heroku or AWS.

  • Your custom app has to use only credentials that the service requires to create a connection. Don't request any additional credentials from the user.

Check your app's code

When developing a custom app in Make, you should first go through our Best Practices guide and apply them to your code. By following our best practices, you will ensure that the review and publication process of your app is as smooth as possible.

Before sending an app for review, check that:

  • The base and connection have sanitization of sensitive data, e. g. API key or token.

  • The base and connection have error handling.

  • The base and connection use an endpoint in the app's API.

  • Connection is using a correct URL. If the user uses incorrect credentials, they get an error.

  • Modules have correct labels and descriptions.

  • The app has a universal module.

  • All modules have the correct interface depending on the output from the module.

  • All dates are formatted or parsed in the mappable parameters and interface.

  • Search modules, trigger modules and RPCs have limit parameter.

  • Search modules, trigger modules and RPCs have the pagination, if it's supported in the service API.

The items above are mandatory for each app.

Create test scenarios

After you check the code of your custom app, you have to create test scenarios to show that the custom app works. Use each module of the custom app in at least one testing scenario.

Make sure that the testing scenarios and their execution logs don't contain personal or sensitive data.

Best practices for test scenarios

In order to help us with a review of your app, please, follow the best practices.

  • Do not use scenarios with another app's webhook.

  • It is best to have all modules in one scenario and run them all without an error. You can group your modules via the entity or create scenarios of the way, the endpoints work together, e.g. Create a Task > Create a Subtask (in order to create a subtask, the task has to be created first).

  • Try to put all app's modules in one scenario and run the scenario without errors. Connect the app modules based on the object the modules work with or the action the modules do. For example, Create a Task > Create a Subtask (in order to create a subtask, the task has to be created first).

Example of a Scenario
  • Put the search and list modules at the end of separate scenario routes to avoid multiple runs of the subsequent modules.

  • Run your search and list modules to have logs with pagination. If you don't know how to test pagination, follow the steps here.

  • Create a separate scenario that produces an error message. Run the scenario to get an error:

Example of Error

Run the test scenarios right before you request the custom app review and every time you fix issues. The scenario execution logs have a data retention limit and the reviewer won't be able to access old logs.

Modules

There are six basic types of modules:

Use if the API endpoint returns a single response. Examples are Create a book, Delete a book or Get a Book.

Use if the API endpoint returns multiple items. An example is List Books that will find specific books according to search criteria.

Use if you wish to watch for any changes in your app/service. Examples are Watch a New Book, which will be triggered whenever a new book has been added to the library.

Use if the API endpoint has a webhook available (dedicated or shared). Example is Watch a New Event.

Use if you want to enable users to perform an arbitrary API call to the service. Examples are Make an API Call and Execute a GraphQL Query.

Use if you need to send a processed data back to a webhook.

Universal Module

Universal Module can be used to perform an arbitrary API call to the service's API. It allows the user to specify all parameters of the request while using the App's connection.

Every app using API should have a Universal module. Each app can have one universal module at most.

Security notice

As the Universal Module allows the user to specify the target URL, it's highly important that the Universal Module has to use a relative path. Otherwise, one could point the request to his own custom servers and get access to the access tokens. So every time use a fixed base URL in this kind of module.

Universal Module which doesn't match this condition won't be approved by Make to be used in scenarios.

There are two types of universal module available. Choose one depending on the API you use:

  • REST API

  • GraphQL API

Components

Components of the Universal Module are the same as for the Action.

Available IML Variables

You can use all of the IML variables available in action modules in the universal module, except for the iterate directive.

Universal in a Scenario

When a universal module is used in a scenario, it is recommended to use it together with JSON > Create JSON module. Not only it is much easier to create the structure of JSON for the universal, but also all characters, which are part of JSON definition and should be considered as letters, are escaped.

Example of Cooperation of JSON > Create JSON and Universal Module

Shared

Shared webhooks come to use when the service sends all the notifications for all the users to only one registered URL.

When the service sends all the notifications to only one webhook URL, but the webhook has to be registered under a user account, that's not a shared webhook.

In order to have the shared webhook listening to incoming traffic, you must publish your app. Before doing so, please, read this article.

Implementation of a shared webhook

In most cases, attach and detach directives don't have to be implemented.

If you still need to implement attach and detach directives, check Dedicated Webhooks.

Shared webhook should be registered by the developer of the app. All notifications from the service for all users will be sent to Make by calling this URL, which is generated when creating the shared webhook. On the Make's end, the corresponding user account will be matched.

You should always follow instructions in API docs of the web service which you are integrating.

Matching the user's account with an incoming event

Since the webhook is shared among multiple users, it is needed to match the incoming events with their owners and deliver them correctly. In order to do it, it is needed to work with uid parameter. Uid parameter can be obtained in connection.

uid specifies how to get the user ID from the request body. This value is then used to search for the recipient of the message in the database of connections. Don't forget to specify the uid parameter in the connection definition.

A connection should be attached to the webhook.

Example

Notice the uid parameter in the response object.

{
	"uid": "{{item.uid}}",
	"output": "{{item.data}}",
	...
}

Notice the uid parameter in the webhook's communication.

Clone Make app to local workspace

Please be advised that this feature is in beta, meaning, it may encounter occasional bugs or inconsistencies, so proceed with an awareness of potential functionality limitations.

To start the development of an app in a local directory or git repository, or start tracking changes in your app, you need to clone the Make app to the local workspace.

Open the local folder

First, you need to open the folder where you intend to store the app, in Visual Studio Code.

Below, please, follow the section that corresponds to your development setup and choose:

  • 'Local Directory' if you are working directly on your computer's file system

  • 'Git Repository' if you are using version control with Git

The 'Git Repository' section describes the development in the Git repository using the app. Yet it's not obligatory, any preferred or CLI can be used.

Local directory
  1. Open Visual Studio Code and navigate to File > Open Folder

  1. In the file manager, choose the folder where the folder with the app should be cloned.

  2. In the pop-up window, click "Yes, I trust the authors" option.

  1. Now, the current local directory in Visual Studio is set.

Git repository using GitHub Desktop app
  1. Navigate to the GitHub Desktop app and open the repository where you intend to store the app.

  2. Click Open in Visual Studio Code button**.**

  1. In the pop-up window, click "Yes, I trust the authors" option.

  1. Now, the current repository in Visual Studio is set.

Clone the app to the local folder

Once the repository in Visual Studio is set, you can proceed to cloning the app to the local folder.

  1. In the opened window of Visual Studio Code, go to Make Apps Editor and right-click the app you wish to save to your repository. Select Clone to Local Folder (beta).

  1. Read the text in the dialog window and confirm reading by clicking Continue.

  1. Enter the workspace subdirectory name, where the app should be cloned to. If the subdirectory doesn't exist yet, it will be created. The default subdirectory is set to src. Click Enter.

If you are going to store more than one app in the repository, you can create a subfolder by using the src/app-name path, where the app-name is the name of the app folder.

  1. A dialog window asking whether should be included or not will pop up.

  • Exclude (more secure) - Select, if your app contains sensitive data, such as Client ID and Secret. Common data will not be stored in your local workspace or a repository.

  • Include (for advanced users only) - Select, if you want to store the common data in your local workspace or a repository. Be aware that storing common data outside of Make could potentially expose the app to vulnerabilities.

  1. The app is now cloned to the local folder.

Start versioning the local app

Versioning is only available if you are using version control with Git.

This manual describes the development in the Git repository using the app. Yet it's not obligatory, any preferred can be used.

The .secrets file with your Make API keys is only stored in the local folder. By default, the file is excluded from the git versioning.

To properly start the versioning of your app in the git repository, follow the steps below:

  1. Go to the GitHub Desktop app and open the repository where you deployed the current version of your app. You will see a list of new files.

  2. Enter the Summary of the commit and click Commit to main.

  3. Now, the first version of your app is logged. Every new change or a new component in the app will be considered a new change.

  1. Optionally, click Publish branch.

Debug IML in VS Code

There're options to debug your IML code locally on your computer.

1. Automated testing with Mocha

The first option is to do automated testing with Mocha. The process and examples can be found in the link below.

2. Debug code in Visual Studio Code

  1. Copy your IML code.

  2. Go to VSC -> File -> New Text File (Shortcut: ⌘ + N - on MacOS, Ctrl + N - Windows/Linux).

  3. Paste your IML function. To execute a code you will need 3 more things:

    1. Input example. You can either type it manually or use debug() inside the IML function on Make and copy input from the developer console of your browser (more about debug() ).

    2. Call function. Since we are testing the function locally, we cannot run a scenario to execute it. To imitate scenario execution we need to call a function by its name and send the input, that we specified before, inside it. Example: myFunction(input) .

    3. Remove all IML functions called inside. All debug() and iml.function() should be removed or replaced by similar JavaScript functions.

  4. Put breakpoints on your code (it's red dots on the left side of line numbers).

  5. Upper menu Run -> Start Debugging -> set Node.js as debug environment

  6. Enjoy! On the left side of VSC, you will see variables that are being processed at the moment. Go through the code step by step and find your bug

  7. If you're interested to explore VSC debug tool check their docs .

In this example, Transport type shouldn't be sent in the request and Year should be sent as a number (not text).

Update modules

Update approaches

Bear in mind that there are two approaches to updating entries in a service.

  • Partial Update - The service updates only specified parameters sent in the API request and other empty parameters will be unchanged. This is the most common approach for APIs.

  • Full Update - The service requires all parameters to be updated in an update request. If some parameters are omitted, then they will be cleared or overridden to default values in the service. This is extremely user-unfriendly and should be avoided.

Handling of full update approach

If the API doesn't support a partial update approach, it is needed to add the support on the app's side. Basically, there should be two calls executed instead of only one:

  • GET call - a call that retrieves the current record and saves it in temp,

  • UPDATE call - an update request which contains the user's input merged with the missing parameters from temp.

Thanks to the handling of the full update approach on the app's side, the user experience will be consistent with all Make apps. Users will not have to handle full updates by themselves or experience data loss.

Example using OR directive

If there are a few parameters available, you can use simple OR (||) directive, which ensures that if there is no value in the particular parameter available, the value from temp is mapped instead.

Communication

Example 2 - IML function

If there are a lot of parameters available, it is worth writing an IML function, which merges the parameters with the output from temp.

Communication

IML Function

Example 3 - considerating read-only parameters

There might be read-only parameters, which can't be updated, e. g. CreatedAt and UpdatedAt parameters. In this case, it is needed to ensure these parameters will be omitted.

You can do it by mapping only the parameters, which are allowed, see the . Or you can implement IML function, see below.

IML Function

Debugging RPC

In the case that you are using RPC inside your app, you might need to debug it. After this article, you will know how to debug RPCs on Make and be an ace of it!

Location of debug tool in RPC

The RPC debug tool can be found by following:

  1. Go to Custom apps tab.

  2. Select your custom app from the list.

  3. Go to the Remore Procedures tab inside the custom app.

  4. Select an RPC you want to debug.

  5. Click Test RPC.

Navigation inside RPC

Compare the tabs below to understand how things work inside RPC on Make.

By default after creating a new RPC, you have a template of the communication code, which should be modified based on your needs.

Inside RPC, you can use the relative path and the full form of the URL. However, we advise you to stick to the relative path across all your RPCs and Modules. A relative path is added to the "baseUrl", that you are supposed to specify inside the App Base (Located: Your App -> Base tab).

Also, there is Parameters tab, which by default is empty. Here you can add any parameter needed. In the same way, you can also do it in the Mappable parameters from a module.

Any parameter created inside an RPC will be available only for RPC debugging. It will not be visible inside your modules or inside scenarios.

To preview and test parameters, click on the Test RPC button.

RPC debug tool

RPC debug tool works the same way modules do.

  1. Specify the connection and other fields (i.e. parameters) if needed.

  2. Click the Test button.

  3. The call, which you specified before in the RPC communication, will be executed.

Notice that you will see the output which you specified in the RPC communication.

If you specified output as "label" and "value"(with the purpose of using it inside a Select parameter), do not expect to see the full server response there.

Sometimes you might get an empty array as a response. If that's not the response that you expected. Check that you correctly specified the path to the object, which you use inside iterate.

Remote Procedure Calls

Use cases of RPCs

Remote procedures are used to get live data from a service. More information about remote procedures can be found .

Use RPCs for every field that accepts parameters like IDs and other stuff that is hard to guess or get for users.

Notice, that in the mapping mode, user has to enter/map option's ID instead of the label.

Also, RPCs come in handy in a case, when a user only needs to understand the functionality of the module, mostly the list of available parameters in the interface. In this case, the aim is not to offer a complete list of all items, but a sample of the last 100 or so items the user could select from when testing the module or setting up the scenario for the first time, before replacing it with mapped data used in automation.

Mode edit

When adding an RPC to a Get, Update, or Delete module, it is required to add:

to the code so when a user first opens the module, they will be able to immediately map the value. However, if needed, they can also switch to getting the value from an RPC.

When it comes to Search/List modules, it depends on the hierarchy level of the entity. If the entity is up in the hierarchy, e.g. a “customer” or a “deal”, and there are not many RPCs in the input, they should not have the mode set to “edit”. However, if the entity is low in the hierarchy, e.g. "E-mail attachment", it should have the mode set to “edit” since the user will probably not want to list attachments of a single e-mail repeatedly.

Finally, a Create module should not have the mode set to "edit", unless it contains a very large number of RPCs in its input. This is because pre-loading a large number of RPCs would significantly increase the waiting time for the user.

Pagination and limits

Just like modules, RPCs can use pagination to show more records than just the ones on the first page. However, limits should be also set on how many objects the RPC shows so it doesn't load forever.

The limit should be between 300-500 if the API returns 100 or more objects per page. If the API returns less than 100 per page, then the limit should be 3 times the amount of objects per page.

For example, if API returns 25 records per page, the limit should be 75. You can also set a condition for the pagination to stop further requests when no more data is needed.

Instant Trigger (webhook)

Instant Trigger is a trigger that is executed immediately when the data arrives to Make.

There is nothing to configure in this module except the interface. The data processing is handled by a selected .

Components

Communication

  • Communication is only optional in the Instant Trigger.

  • It can be used for retrieving additional data.

  • iterate directive is not available.

  • pagination directive is not available.

  • Only a single request can be performed.

Retrieving additional data for each bundle

If you need to retrieve additional data for each bundle, you can describe a request to execute for each bundle of the webhook:

Static Parameters

The Instant Trigger module can only have static parameters. There's no reason to have anything mappable in the Instant Trigger as this module is always the first module in the scenario.

Interface

Exactly one bundle is generated with each incoming webhook.

Samples

To help the users with setting up your module, you can provide samples to it.

Available IML Variables

These IML variables are available for you to use everywhere in this module:

  • now - Current date and time.

  • environment - TBD

  • temp - Contains custom variables created via temp directive.

  • parameters - Contains module’s input parameters.

  • connection - Contains connection’s data collection.

  • common - Contains app’s common data collection.

  • data - Contains module’s data collection.

  • scenario - TBD

  • metadata.expect - Contains module’s raw parameters array the way you have specified it in the configuration.

  • metadata.interface - Contains module’s raw interface array the way you have specified it in the configuration.

Additional variables available to Response Object:

  • output - When using the wrapper directive, the output variable represents the result of the outputdirective.

Additional variables available after using the iterate directive, i.e. in wrapper or pagination directives:

  • iterate.container.first - Represents the first item of the array you iterated.

  • iterate.container.last - Represents the last item of the array you iterated.

Additional variables available to Pagination and Response Objects:

  • body - Contains the body that was retrieved from the last request.

  • headers - Contains the response headers that were retrieved from the last request.

  • item - When iterating this variable represents the current item that is being iterated.

Additional variables available in the Instant Trigger

  • payload - This variable represents the current webhook item that is being processed.

Example

CloudConvert > Convert a File
CloudConvert > Create a Job (advanced)
Convert files from Google Drive in CloudConvert
Convert a File
Create an archive job in CloudConvert
Download files from the job in CloudConvert
Create a Job (advanced)
Action
Search
Trigger (polling)
Instant Trigger (webhook)
Universal
Responder
[
    {
        "url": "/contacts/{{parameters.id}}",
        "method": "GET",
        "response": {
            "temp": {
                "fields": "{{body}}"
            }
        }
    },
    {
        "url": "/contacts/{{parameters.id}}",
        "method": "PUT",
        "body": {
            "name": "{{parameters.name || temp.name}}",
            "email": "{{parameters.email || temp.email}}"
        },
        "response": {
            "output": "{{body}}"
        }
    }
]
[
    {
        "url": "/contacts/{{parameters.id}}",
        "method": "GET",
        "response": {
            "temp": {
                "fields": "{{body}}"
            }
        }
    },
    {
        "url": "/contacts/{{parameters.id}}",
        "method": "PUT",
        "body": {{updateContact(parameters, temp.fields)}},
        "response": {
            "output": "{{body}}"
        }
    }
]
function updateContact (parameters, temp) {
     return {
         ...temp,
         ...parameters
     };
}
function updates (parameters, temp) {

     function omit(obj, ...props) {
        const result = { ...obj };
        props.forEach(function(prop) {
            delete result[prop];
        });
        return result;
    }

     temp = omit(temp, 'id', 'createdAt', 'updatedAt');
     parameters = omit(temp, 'id');
     //or you can use `iml.omit (temp, 'id', 'createdAt', 'updatedAt')`

     return {
         ...temp,
         ...parameters,
         birthday: parameters.birthday ? iml.formatDate(parameters.birthday, 'YYYY-MM-DD') : temp.birthday
         // additionally you can modify extra fields if necessary. 
     };
}
Example using OR Directive
Location of the "Test PRC" button inside every RPC
Default RPC communication template.
Example of modified RPC communication template.
By default RPC is created without any parameters.
Example of RPC with parameters.
RPC debug tool without parameters.
RPC debug tool with parameters.
"mode": "edit",
{
    "name": "userId",
    "type": "select",
    "label": "User ID",
    "options": "rpc://getUsers",
    "mode": "edit"
}
{
    "name": "userId",
    "type": "select",
    "label": "User ID",
    "options": "rpc://getUsers"
}
 {
	"url": "/contacts",
	"method": "GET",
	"qs": {
		"pageSize": 25
	},
	"response": {
		"limit": 75
		"output": {
			"label": "{{item.displayname}}",
			"value": "{{item.id}}"
		},
		"iterate": "{{body.data}}"
	},
	"pagination": {
		"condition": "{{pagination.page <= 3}}",
		"qs": {
			"page": "{{pagination.page}}"
		}
	}
}
 {
	"url": "/contacts",
	"method": "GET",
	"qs": {
		"pageSize": 25
	},
	"response": {
		"output": {
			"label": "{{item.displayname}}",
			"value": "{{item.id}}"
		},
		"iterate": "{{body.data}}"
	},
	"pagination": {
		"qs": {
			"page": "{{pagination.page}}"
		}
	}
}
here
Example of RPC
RPC set to Mapping Mode
Example of List Module with RPC with Mode Edit Set
Example of Missing Limit
GitHub Desktop
GUI tool
common data
GitHub Desktop
GUI tool
Clone to Local Folder (beta) feature
Dialog window requesting the confirmation of recommendations
Setting the workspace subdirectory where the app should be cloned to
Include/Exclude common data dialog
Local folder with the cloned apps
Created new change log record
Best practices
Action
function removeType(input) {
	debug(input)
	delete input.type; //removes type
	input['year'] = iml.parseNumber(input.year); //parses number from text
	return input;
}
function removeType(input) {

    //Initially JavaScript doesn't support debug(),
    //so don't forget to replace it with console.log()
    
    console.log(input); //instead of: debug(input);

    delete input.type;

    console.log(input);

    // JS doesn't support in-build Make functions.
    // If you are used in-build IML functions,
    // don't forget to replace them with JS functions,
    // otherwise you will get an error.

    input['year'] = parseInt(input.year);
    /* instead of: input['year'] = iml.parseNumber(input.year); */

    console.log(input);

    return input;
}

const input = {
    type: 'car',
    make: 'Honda',
    model: 'Civic',
    year: '2022'
};


removeType(input);
here
here
Put breakpoints
Start Debugging
Debugging process
Write IML tests
{
    "url": "http://example.com/api/item/{{payload.id}}",
    "response": {
        "output": {
          "id": "{{payload.id}}",
          "data": "{{body}}"
        }
    }
}
{}
[]
[
	{
		"name": "id",
		"type": "uinteger",
		"label": "User ID"
	},
	{
		"name": "email",
		"type": "email",
		"label": "Email address"
	},
	{
		"name": "name",
		"type": "text",
		"label": "Name"
	},
	{
		"name": "created",
		"type": "date",
		"label": "Date created"
	}
]
{
	"id": 1,
	"email": "[email protected]",
	"name": "John Doe",
	"created": "2018-01-01T12:00:00.000Z"
}
Webhook
Example of an instant trigger
Communication
Static parameters
Interface
Samples
Make API documentation
Overview of error handling
function add(a, b) {
    	let sum = a + b;
    
        //instead of usual console.log(), use debug().
        debug("a = " + a) 
	debug('b = ' + b)
	debug(`a+b = ${sum}`)
	
	return sum;
}
{
    ...
    
    "info": {
        "url": "https://slack.com/api/auth.test",
        "headers": {
            "authorization": "Bearer {{connection.accessToken}}"
        },
        "response": {
            "uid": "{{body.user_id}}",
            "valid": "{{body.ok}}",
            "metadata": {
                "type": "text",
                "value": "{{body.user}}"
            }
        },
        "log": {
            "sanitize": [
                "request.headers.authorization"
            ]
        }
    }
    ...
    
}

Base

The Base section should contain data that is common to all (or most) requests. At the very least, this should include the root URL, the authorization headers, the error-handling section, and the sanitization.

{
    "baseUrl": "https://www.example.com/api/v1",
    "headers": {
        "Authorization": "Bearer {{connection.accessToken}}"
    },
    "response": {
        "error": {
            "message": "[{{statusCode}}] {{body.error.message}} (error code: {{body.error.code}})"
        }
    },
    "log": {
        "sanitize": [
            "request.headers.authorization"
        ]
    }
}

Base URL

Make sure that the Base URL uses the URL of the API, which is shared among all modules or their majority.

In case of a request for approval of your app by Make, make sure that the base URL is a production endpoint with a domain that belongs to the app itself.

Apps with development or staging URLs, or apps with a domain belonging to a cloud computing service, will not be approved!

When the service has a different domain for each user, the domain should be asked for in the connection and then the value should be used in the Base tab.

An example from Mailerlite app:

{
    "baseUrl": "https://api.mailerlite.com/api/v2"
}

An example from Freshsales app:

{
    "baseUrl": "https://{{connection.domain}}.freshsales.io"
}
{
    "baseUrl": "https://mydomain.freshsales.io"
}

The "mydomain" should be a variable used from the Connection.

{
    "baseUrl": "https://mailerlite.heroku.com/development"
}

Authorization and sanitization

The Base section should also have authorization, which is common for all modules. This authorization should use the API Key, Access Token, or Username and Password entered in the connection. The sanitization should hide all these sensitive parameters.

Examples of possible authorization and sanitization:

{
...
    "headers": {
        "Authorization": "Token {{connection.apiKey}}"
    },
    "log": {
        "sanitize": [
            "request.headers.authorization"
        ]
    }
...
}
{
...
    "qs": {
        "access_token": "{{connection.accessToken}}"
    },
    "log": {
        "sanitize": [
            "request.qs.access_token"
        ]
    }
...
}
{
...
    "headers": {
        "authorization": "Basic {{base64(connection.username + ':' + connection.password)}}"
    },
    "log": {
        "sanitize": [
            "request.headers.authorization"
        ]
    }
...    
}
{
...
    "body": "There is something {{parameters.secret}} hidden here.",
    "type": "raw",
    "log": {
        "sanitize": [
            "request.body<parameters.secret>"
        ]
    }
...
}

Error handling

Each service sends an error message when an error occurs. This most of the time happens when the request has wrong parameters or values or when the service has an outage. That's why error handling is required.

In case of a request for approval of your app by Make, make sure error handling is correctly implemented!

The error handling code should correspond to the structure of the server response. Let's assume that the JSON response has the following format in the cases where something goes wrong:

{
    "error": {
        "code": "E101",
        "message": "The company with the given ID does not exist."
    }
}

Here's how the error section should (and should not) look:

"response": {
    "error": {
        "message": "[{{statusCode}}] {{body.error.message}} (error code: {{body.error.code}})"
    }
}

The error object in our example contains the code and message fields. It is also important to show the status code of the error, this can be accessed using the statusCode keyword. So in the case of HTTP error 400, the error message could look like this:

[400] The company with the given ID does not exist. (error code: E101)

"response": {
    "error": {
        "message": "{{body.error.text}}"
    }
}

The error object in our example contains the code and message fields, but not a text field.

Terms of approved app maintenance

When Make approves your custom app any Make user can use your custom app. You have to maintain the app to keep the app working. That involves:

  • updating the custom app when a relevant part of the service API changes

  • fixing bugs the users report

  • submit API checkups

  • checking feature requests on the Make Idea Exchange regularly

When you maintain the custom app actively, the users can use the app with confidence. The users don't have to worry that their scenarios stop working with new service updates.

Fix reported issues

When a user reports an issue, Make validates the issue. If the issue is valid, we contact you via e-mail with a request to fix the reported issue.

At Make, we make sure that everything works for our customers. If you do not fix the validated issue or stop communicating with Make, we will access the app's code and fix it for the app's users.

API Checkup

Make sends you a request to update the custom app's API checkup every six months. The Make API checkup helps you and Make to:

  • watch changes in API and API's docs

  • make sure the app is up-to-date

  • manage deprecations of endpoints or API

  • manage new versions of the API

  • inform users about shutdowns and other cases in advance

The API checkup form contains the following data about the service API:

Parameter
Specification
Example

Currently used API version

The version of the API the app is currently using.

v1, 2019-01

Currently used API's documentation

URL of the API's online documentation.

https://service.com/api/v1

Currently used API's changelog

URL of API's changelog in the API's docs.

https://service.com/api/changelog

Currently used API deprecation date

The date, when the API was or will be deprecated.

01. 02. 2023

Currently used API shutdown date

The date, when the API was or will shut down.

01. 06. 2023

Breaking change in currently used API

Information about the breaking changes in the API.

true, false

Breaking change note

Note about the particular breaking changes in the API.

"Endpoint X has removed parameters. More info in https://...."

Latest API available

Is there a newer API available or not?

true, false

Latest API version

Version of the latest available API.

v2, 2023-01

Latest API's documentation

URL of the latest online documentation of the API.

https://service.com/api/v2

Note

Any note that can help with the evaluation of the app's status or the next API checkup.

"The latest v2 version is currently in beta."

At Make, we make sure that everything works for our customers. If you do not submit the API checkup form or stop communicating with Make, we will take over the custom app maintainership to ensure that Make users can continue using it.

Feature requests on Make Idea Exchange

When Make users miss a feature in an app, they can submit a new request to App Improvement Ideas board. We recommend you check the list of feature requests for your app regularly and respond to them. You can use the search bar for listing requests which mention your app.

App Improvement ideas on the Make Idea Exchange

Connections

Every connection should have a way how to check if the used API Key/Token is valid (Validation Endpoint). That means each connection should have a part that uses the used API Key/Token against some endpoint that requires only the API Key/Token to run. For example, User Info endpoint or any endpoint which is used to List data.

The validation endpoint is located:

  • OAuth1 and OAuth2 - the info directive

  • API Key, Basic Auth, Digest Auth, Other - the url which is in the Communication tab

There are APIs, which don't have a validation endpoint. In this case, it is recommended to call an endpoint, which will work in every case, and if possible will return the account's data, e. g. an endpoint /about or /me, etc.

Coda (beta) connection example

The API Key is checked against /whoami endpoint which in case of wrong API Key returns an error and the connection won't be created.

{
    "authorize": {
        ...
    },
    "token": {
        ...
    },
    "info": {
        "url": "https://api.dropboxapi.com/2/users/get_current_account",
        "method": "POST",
        "headers": {
            "authorization": "Bearer {{connection.accessToken}}"
        },
        "body": "{{null}}",
        "response": {
            "uid": "{{body.account_id}}",
            "metadata": {
                "type": "email",
                "value": "{{body.email}}"
            },
            "error": {
                "message": "[{{statusCode}}] {{if(body.error_summary, body.error_summary, body)}}"
            },
            "data": {
                "root_namespace_id": "{{body.root_info.root_namespace_id}}"
            }
        },
        "log": {
            "sanitize": [
                "request.headers.authorization"
            ]
        }
    },
    "invalidate": {
        ...
    }
}

An example of the "info" part of the Dropbox Oauth2 connection.

There is no endpoint to check whether entered credentials like API Key, Username & Password, etc. were correct and will allow the connection to be created with anything, including wrong credentials.

Connection metadata

It is recommended to use the metadata parameter to store the account's name or email. This allows users to easily distinguish their stored connections, especially if they don't name their connections in a good manner.

When a string is stored as metadata, Make sets a limit of 512 characters.

Example of Connection's Name

Notice the value in the brackets after the user's connection name. This value is taken from the metadata parameter.

...
"response": {
    "metadata": {
            "type": "email", //allowed values are "email" and "text"
            "value": "{{body.data.user.email}}"
        }
},
...

Editable connection

We recommend allowing users to edit their connections after they create them. Updating a connection simplifies scenario and user credential maintenance when there's a change in the user's organization. You can read more about editable connections here.

To allow users to edit a connection:

  1. Go to the Parameters tab of the connection.

  2. Check that each parameter which original value should be kept secret has the password type.

    The parameters with the password type don't show the original connection's parameter value, while the parameters with the text type show the value used by the current connection.

  3. Add the editable: true property for each parameter in the connection.

For example:

[
    {
        "help": "Your MailerLite API Key.",
        "name": "apiKey",
        "type": "password",
        "label": "API Key",
        "editable":true,
        "required": true
    }
]

Exception: If the service provides each user with a unique URL or domain, the corresponding URL or domain parameter must be non-editable to prevent any potential credential leaks.

Error handling

Error handling can be used from the Base tab and follows the same rules.

The only difference is where to use it in what type of connection. For example, in connection types OAuth 1.0 and OAuth 2.0, the error handling should be in the "info" part.

Action and search modules

Suppose you want to retrieve all users, that are registered on your service. You can’t use Action, because it returns only a single result. You will have to create a Search module for this.

The communication for Search is the same as for Action, except Search has an iterate directive, which specifies where are the items located inside the body.

For the next example, suppose that when you call /users on your service, you will get a list of users in body.data.

This example will correctly output each user that was returned:

{
    "url": "/users",
    },
    "response": {
        "output": "{{item}}",
        "iterate": "{{body.data}}",
        "limit": "{{parameters.limit}}"
    }
}

Iteration and pagination

An Action module should never contain pagination or the iterate directive. If you need to return multiple objects, create a Search module instead.

Example of Wrong Implementation of Search Endpoint as Action Module
Example of Correct Implementation of Search Module and Pagination Directive

Pagination parameters

The pagination section should only contain parameters directly influencing the actual pagination. These will be merged with the rest of the parameters defined in the qs section, so there is no need to define them all again.

Example of Wrong Implementation of Pagination Directive

The pagination directive contains "since", "until" and "limit" parameters that are already defined in query string ("qs").

Example of Correct Implementation of Pagination Directive

The pagination contains only the "offset" parameter.

Limiting output

The modules type Search and Trigger(polling) should return everything including by pagination. However, these modules should also allow users to limit their output, that is, how many bundles they return.

This can be achieved by setting up the limit parameter in the response. By default, this parameter is added to the Trigger (polling) modules and should be required. In Search modules, this parameter should NOT be required so if a user leaves it empty, the Search modules return everything. Its default value should be set to 10.

Search module limit example:

{
    "url": "/clients",
    "method": "GET",
    "qs": {
        "per_page": 100
    },
    "response": {
        "limit": "{{parameters.limit}}",
        "output": "{{item}}",
        "iterate": "{{body.clients}}"
    },
    "pagination": {
        "qs": {
            "page": "{{pagination.page}}"
        },
        "condition": "{{body.next_page}}"
    }
}
[
    {
        "name": "limit",
        "label": "Limit",
        "type": "uinteger",
        "help": "Number of clients to return",
        "default": 10
    }
]

The limit parameter should not be set to "required":true (except for polling trigger modules). The limit parameter should never be set to "advanced":true.

Search

The Search Module is a module that makes a request (or several) and returns multiple results. It doesn’t have state nor any internal complex logic.

Use this module when you need to allow the user to search for items or simply return multiple items.

Components

Communication

Pagination

If API supports pagination, you can implement it by using pagination directive.

Static Parameters

You can use static parameters inside the Search module without any restrictions.

Mappable Parameters

You can use mappable parameters inside the Search module without any restrictions.

Interface

Unlike the Action module, the Search module can return multiple bundles at once.

Samples

To help the users with setting up your module, you can provide samples to it.

​Scope​

When using an OAuth type of connection, use the Scope to define scopes required by this module.

Available IML Variables

The IML variables are variables that you are able to use in IML expressions.

These IML variables are available for you to use everywhere in this module:

  • now - Current date and time.

  • environment - TBD

  • temp - Contains custom variables created via temp directive

  • parameters - Contains module’s input parameters.

  • connection - Contains connection’s data collection.

  • common - Contains app’s common data collection.

  • data - Contains module’s data collection.

  • scenario - TBD

  • metadata.expect - Contains module’s raw parameters array the way you have specified it in the configuration.

  • metadata.interface - Contains module’s raw interface array the way you have specified it in the configuration.

Additional variables available to Response Object:

  • output - When using the wrapper directive, the output variable represents the result of the outputdirective

  • limit - When you use a limit, the process of retrieving items will stop once either the requested number of items has been obtained or if a page doesn't contain any items. Additionally, the module will return only the exact number of items that was specified.

  • iterate - Iterates the array in the response into items.

Additional variables available after using the iterate directive, i.e. in wrapper or pagination directives:

  • iterate.container.first - Represents the first item of the array you iterated

  • iterate.container.last - Represents the last item of the array you iterated

Additional variables available after using the iterate directive, i.e. in wrapper or pagination directives:

  • body - Contains the body that was retrieved from the last request.

  • headers - Contains the response headers that were retrieved from the last request.

  • item - When iterating this variable represents the current item that is being iterated.

Example

Example of search module
{

	"url": "/api/users",
	"qs": {
		"search": "{{parameters.search}}"
		},
	"method": "GET",
	"response": {
		"output": "{{item}}",
		"iterate": "{{body.users}}",
		"limit": "{{parameters.limit}}"
	}
}
[]
[
	{
		"name": "search",
		"type": "text",
		"label": "Search",
		"required": true
	},
	{
		"name": "limit",
		"type": "uinteger",
		"label": "Limit",
		"help": "Maximum number of results.",
		"default": 10
	}
]
[
	{
		"name": "id",
		"type": "uinteger",
		"label": "User ID"
	},
	{
		"name": "email",
		"type": "email",
		"label": "Email address"
	},
	{
		"name": "name",
		"type": "text",
		"label": "Name"
	},
	{
		"name": "created",
		"type": "date",
		"label": "Date created"
	}
]
{
	"id": 1,
	"email": "[email protected]",
	"name": "John Doe",
	"created": "2018-01-01T12:00:00.000Z"
}

Module Actions

Optionally, you can define the module's action to take advantages of features, read more below.

Create

Used for modules that are creating an object, most of the time, these modules use POST request.

Module: Create a Contact

{
    "url": "/contacts",
    "method": "POST",
    "body": {
    "{{...}}": "{{omit(parameters, 'date')}}",
    "date": "{{formatDate(parameters.date, 'YYYY-MM-DD')}}"
    },
    "response": {
        "output": "{{body}}"
    }
}

Module: Create a Contact

{
    "url": "/contacts",
    "method": "POST",
    "body": "{{parameters}}",
    "response": {
        "output": "{{body}}"
    }
}
{
    "url": "/contacts"
    "method": "POST",
    "qs": {},
    "headers": {},
    "body": {
        "name": "{{parameters.name}}",
        "email": "{{parameters.email}}",
        "phone": "{{parameters.phone}}",
        "address": "{{parameters.address}}"
    },
    "response": {
        "output": "{{body}}"
    }
}

There are two types of responsiveness - synchronous and asynchronous. Read more about it in Responsiveness approaches.

Read

Used for modules that are retrieving an object, most of the time, these modules use GET request.

Module: Get a Contact

{
    "url": "/contacts/{{parameters.contact_id}}",
    "method": "GET",
    "response": {
        "output": "{{body}}"
    }
}

There is a difference between List/Search and Get modules although they use the same method "GET".

List/Search modules return multiple bundles and should be Module type: Search.

Get modules return only 1 bundle (specified by the entered ID) and should be Module type: Action.

"Invalid module output. Expected Object, but found Array." error

If you happen to receive this error: Invalid module output. Expected Object, but found Array., it means that your module should be type Search. Type search expects an output type array, and unlike the type action supports pagination directive.

Data Error "Invalid module output."
Options > Edit Module

If you don't want to iterate the array returned from the API, you can wrap it in an object:

 "response": {
            "output":
            {
                "myArray": "{{body}}"
            }
        }

Update

Used for modules that are updating an object, most of the time, these modules use PATCH or PUT request.

When a module is type Update, a new keyword appears inside Make - erase.

Input dialog - keyword "erase"

Module: Update a Contact

{
    "url": "/contacts/{{parameters.contact_id}}",
    "method": "PUT",
    "body": {
        "name": "{{parameters.name}}",
        "email": "{{parameters.email}}",
        "phone": "{{parameters.phone}}",
        "address": "{{parameters.address}}"
    },
    "response": {
        "output": "{{body}}"
    }
}
{
    "url": "/contacts/{{parameters.contact_id}}",
    "method": "PUT",
    "body": "{{parameters}}",
    "response": {
        "output": "{{body}}"
    }
}

There are two types of update approaches - partial and full. Read more about it in Update approaches.

Delete

Used for modules that are deleting an object, most of the time, these modules use DELETE request.

Module: Delete a Contact

{
    "url": "/contacts/{{parameters.contact_id}}",
    "method": "DELETE",
    "qs": {},
    "headers": {},
    "body": {},
    "response": {
        "output": "{{body}}"
    }
}

Modules

Module types

Modules should be associated with the correct type, depending on their functionality. Detailed descriptions of different types of modules can be found in our Modules docs as well as in the Best Practices guide.

Module of Type Action
Module of Type Search

Universal module

Each app should have a universal module. This module is there to allow users to use not supported API endpoints using their connection created to the app.

More about this module can be found here.

Make sure this module has:

  • correct label and description,

  • correct url which starts with the base URL,

  • correct connection.

Using the base URL

All of the modules should build on top of baseURL from the Base section (simply by starting with a forward slash /). It is very unlikely that a single module will need to have a completely different URL than the rest.

Example of Wrong URL

The underlined part which is the same for each module should be in the Base tab.

Example of Correct URL

Modules "url" should start with forward slash /

Make DevTool

Connection

APIs usually employ some sort of authentication/authorization to limit access to their endpoints.

Make platform provides you with a list of the most used types of connections while in every each there is a code prefilled. You only need to edit it up to your and/or API's needs.

Mostly, you will need to change the URLs and names of the parameters.

Setting up a connection for our demo API

While the /helloworld endpoint was accessible without any authentication/authorization, the other endpoints of the Demo API will require an API key.

Let’s try to call endpoint /books that should respond with a list of books in the library:

Without providing the API key, the response will contain the following error:

To enable the user of your Module to specify her/his own API key (assuming each user has got her/his own API key to access the API), you need to create a Connection.

We have covered the basics of creating a simple module. Since the /books endpoint will always return an array with items, you will need to create a new module type Search. Now, let’s see how to update our search module with a variable API token for each user.

Click the tab Connections. The (probably still empty) list of all your connections will be shown. Click the large button with the plus sign and choose Create a new Connection. A dialog will pop up, where you can name your connection and choose its type. Fill the dialog as shown and click Save.

The new Connection will appear in the list. Click the new Connection. A page with two tabs will be shown: COMMUNICATION and PARAMETERS.

A pre-configured communication will look like this:

The COMMUNICATION section specifies a simple request to determine whether the credentials entered by the user are valid or not. The most common way to validate the credentials is to call an endpoint to get the user’s information and/or tokens, if available. Most of the APIs have such an endpoint.

There are types of connections, e. g. API Key, which don't have such an endpoint. In this case, it is recommended to call an endpoint, which will work in every case, and if possible will return the account's data, e. g. an endpoint /about or /me, etc.

Not only the credentials entered by the user will be validated, but also the account's name or email can be stored in the name of the connection (the value in brackets after the user's connection name), see the example below.

Since our Demo API doesn't have any suitable endpoint, we will not use any.

The code should be as below:

Note that we used sanitization for API tokens. Always sanitize personal information, like tokens, keys and authentication secrets so they are not visible to other people.

Always use sanitization in base and connection! Learn more about sanitization .

Also note the password type of the apiKey parameter. The password type hides the content of the apiKey field when the app users create or edit the connection.

You can read more about the password parameter type .

Once you finish the connection configuration, you can go back to your search module and click Attach Connection.

An Attach Connection dialog will appear. There, select the currently created connection.

When we are setting up a connection, we should not forget about the base!

As you remember, in base, there should be everything common to all modules and , e. g. baseUrl, authorization, sanitization, error handling, etc.

Therefore, click BASE tab and edit the code:

Notice that not only we added a new header with authorization (we mapped the apiKey from connection), but also edited the sanitization.

Awesome! You just learned how to add a new connection, attach it to an existing module, and map the connection data in base. Now, it is the right time to learn, how to make error handling, continue below.

Components

Components

Communication

  • Communication response is extended with wrapper object.

  • limit is not available in response as the result of action should always be only one bundle

  • Communication can be .

response.wrapper

Required: no Default: output

This directive lets you post-process module output before returning it to the user. The output of the module will be available to you as the output context variable - the result of processing the output directive. When used, the value of the wrapper directive is what will become the final output of the module. This directive is executed only once and at the end of the processing chain. There are no more directives or transformations after it.

Sample usage of the wrapper directive:

As you can see. After the response is returned, first, the iterate directive is called and it starts iterating body.users and pushes them to the output. Then the wrapper directive is processed and it wraps the original output, which is an array of iterated values, with some additional values. The wrapper is then an actual output of the module.

Static parameters

You can use static parameters inside the Action module without any restrictions.

Mappable parameters

You can use mappable parameters inside the Action module without any restrictions.

Interface

Remember that the Action module should always output only one bundle.

Samples

To help the users with setting up your module, you can provide samples to it.

Scope

When using an OAuth type of connection, use the Scope to define scopes required by this action.

Available IML variables

These IML variables are available for you to use everywhere in this module:

  • now - Current date and time

  • environment - TBD

  • temp - Contains custom variables created via temp directive

  • parameters - Contains module’s input parameters.

  • connection - Contains connection’s data collection.

  • common - Contains app’s common data collection.

  • data - Contains module’s data collection.

  • scenario - TBD

  • metadata.expect - Contains module’s raw parameters array the way you have specified it in the configuration.

  • metadata.interface - Contains module’s raw interface array the way you have specified it in the configuration.

Additional variables available to Response Object:

  • output - When using the wrapper directive, the output variable represents the result of the outputdirective

Additional variables available after using the iterate directive, i.e. in wrapper or pagination directives:

  • iterate.container.first - Represents the first item of the array you iterated

  • iterate.container.last - Represents the last item of the array you iterated

Additional variables available to Pagination and Response Objects:

  • body - Contains the body that was retrieved from the last request.

  • headers - Contains the response headers that were retrieved from the last request.

  • item - When iterating this variable represents the current item that is being iterated.

Example

GraphQL

Naming Convention

As we want to keep the naming of the Universal Modules the same across all Apps which support them, the Universal Module should keep the following guidelines.

Module label: Execute a GraphQL Query

Module description: Performs an arbitrary authorized GraphQL query.

Example:

Error handling

Since any API endpoint can return an error during an execution of a scenario, Make brought a way, how to handle such errors. Therefore, all apps in Make, custom included, should have error handling implemented. This way, the modules are more transparent as users of them exactly know, what caused the error. Also, error handling in a scenario can be used.

Read more about error handling in Make:

Error handling should always be designed accordingly to the way, how API returns the error. You can always check the Integromat DevTool or Console and see the structure of the error.

When the service returns an HTTP error, it is not possible to evaluate it as a success.

Example of error handling

Notice, that the wording of the error is now available. Thanks to this, you know where is an error and how to solve/handle it.

HTTP 4** and 5** error handling

When the response is returned with 4** or 5** HTTP Status Code, this is automatically considered an error. If the error directive is not specified, the user will see a message for the status code that was returned (). But you are able to customize the message, shown to the user with the error or error.message directive.

Example:

HTTP 2** and 3** error handling

Some APIs signal an error with a 200 status code and a flag in the body. For this scenario, there is the validdirective, which tells whether the response is or not.

Custom error handling based on status codes

You are also able to further customize what error message will be shown to the user based on the status code. To do that, just add your status code to the error directive and fill it in as one:

Available error types in apps

When handling an error, you can specify the type of the error:

  • UnknownError

  • RuntimeError (default)

    • Primary error type. Execution is interrupted and rolled back.

  • InconsistencyError

  • DataError

    • Incoming data is invalid. If incomplete executions are enabled, execution is interrupted and the state is stored. The user is able to repair the data and resume execution.

  • RateLimitError

    • Service responded with rate-limit related error. Applies delay to the next execution of a scenario.

  • OutOfSpaceError

    • The user is out of space.

  • ConnectionError

    • Connection-related problem. Applies delay to the next execution of a scenario.

  • InvalidConfigurationError

    • Configuration-related problem. Deactivates the scenario and notifies the user.

  • InvalidAccessTokenError

    • Access token-related problem. Deactivates the scenario and notifies the user.

  • UnexpectedError

  • MaxResultsExceededError

  • IncompleteDataError

    • Incoming data is incomplete.

  • DuplicateDataError

    • Reports error as warning does not interrupt execution. If incomplete executions are enabled, execution is interrupted and the state is stored. The user is able to repair the data and resume execution.

Example of error handling with type

Base

Base serves as the repository for all components that are common across all modules and remote procedures. Any elements placed in Base will be inherited by each module and remote procedure.

These components are:

Example

Everything specified in the base is inherited by all modules and RPCs. You can see the baseUrl, authorization being sent in headers, an error handler and a sanitization. Those parameters will be used across all the modules and RPCs.

Common Data

When you want the Common Data to be available only to the connection (for example for storing OAuth secrets), use instead.

Once the app becomes Approved, the Common Data gets locked and it cannot be changed anymore due to security reasons.

Common Data can be accessed by common.variable IML expression.

Common data are stored in encrypted form in Make.

Example

As you can see, the secret is defined in the common data. Then it can be used in base and in all other communication objects inside modules and RPCs. Once the app becomes approved, it will not be possible to change the secret.

By default, requests time out after 40 seconds. If your API typically performs tasks that exceed this duration, you can increase the timeout—up to a maximum of 300 seconds (or 300,000 milliseconds).

To adjust the timeout, specify the desired duration in the base and common data settings (in milliseconds). This timeout will apply to the entire custom app. We recommend the following approach (example using a 300 seconds timeout):

The timeout should only be extended if the API performs legitimately resource-intensive operations, such as video/image processing, file conversion, or complex AI computations, etc.

Authorization

The Base section is used for setting up the authorization. The most of services require the authorization key to be sent either in headers or in the qs (query-string). If you set the authorization in the Base all modules and RPCs will inherit it.

Examples

The most common ways of authorizing are:

API key in headers

API key in query string

OAuth 2 access token in headers

"response": {
    "output": "{{item}}",
    "iterate": "{{body}}"
}
"response": {
    "output": {
        "id": "{{item.id}}",
        "name": "{{item.name}}"
    },
    "iterate": "{{body}}"
}
"response": {
    "output": "{{body}}"
}
"response": {
    "output": {
        "id": "{{body.id}}",
        "name": "{{body.name}}"
    }
}
{
    "headers": {
        "x-api-key": "{{connection.apiKey}}"
    }
}
{
    "qs": {
        "apikey": "{{connection.apiKey}}"
    }
}
{
    "headers": {
        "authorization": "Bearer {{connection.accessToken}}"
    }
}
{"error":"Invalid or missing API key"}
{
    "url": "https://www.example.com/api/whoami",
    "headers": {
        "x-api-key": "{{parameters.apiKey}}"
    },
    "log": {
        "sanitize": ["request.headers.x-api-key"]
    }
}
...
"response": {
    "metadata": {
            "type": "email",
            "value": "{{body.data.user.email}}"
        }
},
...
{
    "headers": {
    "api-token": "{{parameters.apiKey}}"
        },
    "log": {
        "sanitize": ["request.headers.api-token"]
    }
}
[
    {
        "name": "apiKey",
        "type": "password",
        "label": "API Key",
        "required": true,
        "editable": true
    }
]
{
    "baseUrl": "http://demo-api.integrokit.com/api/v1",
    "headers": {
        "api-token": "{{connection.apiKey}}"
    },
    "log": {
    "sanitize": ["request.headers.api-token"]
    }
}
http://demo-api.integrokit.com/api/v1/books
here
here
remote procedures
Create a new Connection panel
A New Connection Dialog
Example of Connection's Name
Attach Connection
Selection of Connection
{
    "url": "https://www.example.com/graphql",
    "method": "{{parameters.method}}",
    "qs": {
		"query": "{{parameters.queryQs}}"
	},
    "headers": {
		"Content-Type": "application/json"
	},
	"body": {
		"query": "{{parameters.queryBody}}",
		"operationName": "{{parameters.operationName}}",
		"variables": "{{if(isArray(parameters.variables), toCollection(parameters.variables, 'key', 'value'), parameters.variables)}}"
	},
	"type": "json",
    "response": {
        "output": {
            "headers": "{{headers}}",
            "body": "{{body}}",
			"statusCode": "{{statusCode}}"
        }
    }
}
[
    {
        "name": "method",
        "type": "select",
        "label": "Method",
        "required": true,
        "default": "POST",
        "options": [
            {
                "label": "GET (introspection query)",
                "value": "GET",
				"nested": [
					{
						"name": "queryQs",
						"label": "Query",
						"type": "text",
						"required": true
					}
				]
            },
            {
                "label": "POST (queries and mutations)",
                "value": "POST",
				"nested": [
					{
						"name": "queryBody",
						"label": "Query",
						"type": "text",
						"required": true
					},
					{
						"name": "operationName",
						"label": "Operation name",
						"type": "text",
						"advanced": true
					},
					{
						"name": "variablesDataSource",
						"label": "Variables data source",
						"type": "select",
						"required": true,
						"advanced": true,
						"default": "array",
						"options": [
							{
								"value": "array",
								"label": "Form",
								"nested": [
									{
										"name": "variables",
										"label": "Variables",
										"type": "array",
										"mappable": {
											"help": "Key-Value pairs as an Array of Collections e.g. `[{key:id,value:123},{key:name,value:'John'},…]`"
										},
										"spec": [
											{
												"name": "key",
												"label": "Key",
												"type": "text"
											},
											{
												"name": "value",
												"label": "Value",
												"type": "text"
											}
										]
									}
								]
							},
							{
								"value": "object",
								"label": "Collection",
								"nested": [
									{
										"name": "variables",
										"label": "Variables",
										"help": "A single Collection e.g. `{id:123,name:'John', …}`",
										"type": "any"
									}
								]
							}
						]
					}
				]
            }
        ]
    }
]
[
	{
		"name": "body",
		"type": "any",
		"label": "Body"
	},
	{
		"name": "headers",
		"type": "collection",
		"label": "Headers"
	},
	{
		"name": "statusCode",
		"type": "number",
		"label": "Status code"
	}
]
{}
Example of creation of a new Graph QL module for Make app
Example of GraphQL Query module
Base URL
Authorization
Sanitization
Error handling
Error handling
Communication
Pagination
Static parameters
Mappable parameters
Interface
Samples
Scope
{
	"url": "/api/users/",
	"method": "GET",
	"response": {
		"iterate": "{{body.users}}",
		"output": {
			"label": "{{item.name}}",
			"value": "{{item.id}}"
		},
		"wrapper": {
			"someAdditionalStuff": "helloworld",
			"aNumber": "{{body.aValue}}",
			"data": "{{output}}"
		}
	}
}
{
	"url": "/api/users/create",
	"body": {
		"name": "{{parameters.name}}",
		"email": "{{lower(parameters.email)}}"
	},
	"method": "POST",
	"response": {
		"output": {
			"id": "{{body.id}}"
		}
	}
}
[]
[
	{
		"name": "email",
		"type": "email",
		"label": "Email address",
		"required": true
	},
	{
		"name": "name",
		"type": "text",
		"label": "Name",
		"required": true
	}
]
[
	{
		"name": "id",
		"type": "uinteger",
		"label": "User ID"
	}
]
{
	"id": 1
}
request-less
Example of action module
Communication
Static parameters
Mappable parameters
Interface
Samples
Scope
{
    "baseUrl": "",
    "headers": {},
    "qs": {},
    "body": {},
    "response": {},
    "log": {
        "sanitize": []
        },
    "oauth": {}
}

Key

Type

Description

baseUrl

String

If you want to use this base URL in a request, you need to start the URL of an endpoint with / character.

headers

Object

Default headers that every module will use.

qs

Object

Default query string parameters that every module will use.

body

Object

Default request body that every module will use when issuing a POST or PUT request.

response

Object

Default directives for handling response, such as error handling.

log

Object

Default directive for handling logs, such as sanitization of sensitive data.

oauth

Oauth 1 Parameter Specification

Collection of directives containing parameters for the OAuth 1 protocol.

{
    "baseUrl": "https://my.api.cz/2.0",
    "headers": {
        "authorization": "Basic {{base64(connection.username + ':' + connection.password)}}"
    },
    "response": {
        "valid": {
            "condition": "{{body.status != 'error'}}"
        },
        "error": {
            "200": {
                "message": "{{ifempty(errors(body.message), body.message)}}"
            },
            "message": "[{{statusCode}}]: {{body.reason}}"
        }
    },
    "log": {
        "sanitize": [
            "request.headers.authorization"
        ]
    }
}
{
	"secret": "AABBCCDD"
}
{
	"baseUrl": "https://www.example.com",
	"headers": {
		"Signature": "{{common.secret}}"
	}
}
{
	"timeout": 300000
}
{
	"url": "https://www.example.com",
	"method": "GET",
	"timeout": "{{common.timeout}}"
}
Connections' common data
Base URL
Authorization
Error handling
Sanitization
Logo
{
  ...
  "response": {
		 "error": {
            	     "message": "[{{statusCode}}] {{body.error}}"
        }},
  ...
}
{
    "response": {
        "error": {
            "type": "RuntimeError",
            "message": "[{{statusCode}}] {{body.error.message}}",
            "400": {
                "type": "DataError",
                "message": "[{{statusCode}}] {{body.error.message}}"
            },
            "500": {
                "type": "ConnectionError",
                "message": "[{{statusCode}}] {{body.error.message}}"
            }
        }
    }
}
{
    "response": {
            "valid": {
                "condition": "{{body.status != 'error'}}"
            },
            "error": {
                "200": {
                    "message": "{{ifempty(errors(body.message), body.message)}}"
                },
                "message": "[{{statusCode}}]: {{body.reason}}"
            }
        }
}
{
    "response": {
        "error": {
            "message": "Generic error message",
            "400": {
                "message": "Your request was invalid"
            },
            "500": {
                "message": "The server was not able to handle your request"
            }
        }
    }
}
{
    "response": {
        "error": {
            "type": "DataError",
            "message": "[{{statusCode}}] {{body.message}}"
        }
    }
}
Introduction to error handling
Directives for error handling
Advanced error handling
List of HTTP status codes
Error in Integromat DevTool
Error with no detail message
Error with a detail message
Replacing Legacy Modules with New Modules

OAuth 1.0

Connection is a link between Make and 3rd party service/app. OAuth 1.0 connection handles the token exchange automatically.

Before you start configuring your OAuth 1.0 connection, you need to create an app on a 3rd-party service. When creating an app, use https://www.integromat.com/oauth/cb/app-oauth1 as a callback URL.

Components

Communication

  • aws directive is not available

  • Communication is extended with oauth

  • pagination directive is not available

  • response.limit is not available

  • response.iterate directive is not available

  • response.output is not available

  • response is extended with data

oauth

It is sometimes very tedious and hard to generate an OAuth 1.0 Authorization header. So we have provided a helper directive, that will simplify this task. Below are all the properties that you can use to customize the header generation.

Key

Type

Description

consumer_key

IML String

Your consumer key

consumer_secret

IML String

Your consumer secret

private_key

IML String

Instead of consumer_secret you can specify a private_key string in PEM format

token

IML String

An expression that parses the oauth_token string.

token_secret

IML String

An expression that parses the oauth_token_secret string.

verifier

IML String

An expression that parses the oauth_verifier string.

signature_method

String

Specifies the desired method to use when calculating the signature. Can be either HMAC-SHA1, RSA-SHA1, PLAINTEXT. Default is HMAC-SHA1.

transport_method

String

Specifies how OAuth parameters are sent: via query params, header or in a POST body. Can be either query, body or header. Default is header

body_hash

IML String

To use Request Body Hash, you can either manually generate it, or you can set this directive to true and the body hash will be generated automatically

response.data

The data directive saves data to the connection so that it can be later accessed from a module through the connection variable. It functions similarly to the temp directive, except that data is persisted to the connection.

Example:

{
    "response": {
        "data": {
            "accessToken": "{{body.token}}"
        }
    }
}

This accessToken can be later accessed in any module that uses this connection like so:

{
    "url": "http://example.com",
    "qs": {
        "token": "{{connection.accessToken}}"
    }
}

​Parameters​

Parameters the user needs to provide when setting up a new connection.

Default Scope

Default scope for every new connection.

Scope List

Collection of available scopes.

​Common Data​

Non-user-specific sensitive values like salts or secrets.

OAuth 1.0 Authentication Process

OAuth 1.0 authentication process consists of multiple steps. You are able to select the steps you need and ignore the steps that you don’t - just fill in the needed sections and delete unneeded ones.

Key

Type

Description

OAuth 1 Parameters Specification

Allows you to specify special OAuth 1.0 properties to simplify OAuth 1.0 header generation.

requestToken

Request Specification

Describes a request that retrieves the request token

authorize

Request Specification

Describes authorization process.

accessToken

Request Specification

Describes a request that exchanges credentials and the request token for the access token.

info

Request Specification

Describes a request that validates a connection. The most common way to validate the connection is to call a method to get user’s information. Most of the APIs have such a method.

When using an OAuth 1.0 connection there is a special object available globally: the oauth object. You can use it in connection specification as well as in module specification to avoid generating the OAuth 1.0 header yourself. This object is available at the root of the connection specification, in the Base and in Request Specification

If the oauth object is present in the root of the connection specification, it will be merged with each of the directives described above. If you wish to override some properties of the root object, you can do so in the respective directive by specifying the oauth object and overriding the properties.\

Available IML Variables

These IML variables are available for you to use everywhere in this module:

  • now - Current date and time

  • environment - TBD

  • temp - Contains custom variables created via temp directive.

  • parameters - Contains connection’s input parameters.

  • common - Contains connection’s common data collection.

  • data - Contains connection's data collection.

  • oauth.scope - Contains an array of scope required to be passed to OAuth 1.0 authorization process.

  • oauth.redirectUri - Contains redirect URL for OAuth 1.0 authorization process.

OAuth 2.0

Connection is a link between Make and 3rd party service/app. OAuth 2.0 connection handles the token exchange automatically.

Before you start configuring your OAuth 2.0 connection, you need to create an app on a 3rd-party service.

When creating an app, use:

  • https://www.make.com/oauth/cb/app as a callback URL together with oauth.makeRedirectUrivariable, or:

  • https://www.make.com/oauth/cb/app as a callback URL together with oauth.localRedirectUrivariable, if you are going to request approval of your app, or:

  • https://www.integromat.com/oauth/cb/app as an old callback URL together with oauth.redirectUri variable.

Components

Communication

  • aws directive is not available

  • pagination directive is not available

  • response.limit is not available

  • response.iterate directive is not available

  • response.output is not available

  • response is extended with data

  • response is extended with expires

response.data

The data directive saves data to the connection so that it can be later accessed from a module through the connection variable. It functions similarly to the temp directive, except that data is persisted to the connection.

Example:

{
    "response": {
        "data": {
            "accessToken": "{{body.token}}"
        }
    }
}

This accessToken can be later accessed in any module that uses this connection like so:

{
    "url": "http://example.com",
    "qs": {
        "token": "{{connection.accessToken}}"
    }
}

response.expires

The expires directive says, when the refresh token (or whole connection when there's no refresh token) will expire. Don't change this with response.data.expires which is telling you when the current access token will need to be refreshed**.** When the expires period is overdue, the connection needs to be reauthorized manually. This can be done either from a scenario or the "Connections" tab.

Example:

{
    "response": {
        "expires": "{{addDays(now, 30)}}"
    }
}

​Parameters​

Parameters that the user should fill while creating a new connection.

​Default Scope​

Default scope for every new connection.

​Scope List​

Collection of available scopes.

​Common Data​

Non-user-specific sensitive values like salts or secrets.

OAuth 2.0 Authentication Process

OAuth 2.0 authentication process consists of multiple steps. You are able to select the steps you need and ignore the steps that you don’t - just fill in the needed sections and delete unneeded ones.

Key

Type

Description

preauthorize

Request Specification

Describes a request that should be executed prior to authorize directive.

authorize

Request Specification

Describes authorization process.

token

Request Specification

Describes a request that exchanges credentials for tokens.

info

Request Specification

Describes a request that validates a connection. The most common way to validate the connection is to call an API’s method to get user’s information. Most of the APIs have such a method. info directive can be used to store account's metadata.

refresh

Request Specification

Describes a request that refreshes an access token.

invalidate

Request Specification

Describes a request that invalidates acquired access token.

Each section is responsible for executing its part in the OAuth 2.0 flow.

In short, you can describe the initial OAuth 2.0 flow as follows:

preauthorize => authorize => token => info

with preauthorize and info sections being optional, and refresh and invalidate not being a part of the initial OAuth 2.0 flow.

If the authorize directive isn't used, the condition in thetoken directive has to be set totrue.Otherwise, the token directive will not be successfully triggered.

Available IML variables

These IML variables are available for you to use everywhere in this module:

Name
Description

now

Current date and time.

environment

TBD

temp

Contains custom variables created via temp directive.

parameters

Contains connection’s input parameters.

common

Contains connection’s common data collection.

data

Contains connection’s data collection.

oauth.scope

Contains an array of scope required to be passed to OAuth 2.0 authorization process.

oauth.redirectUri

Contains redirect URL for OAuth 2.0 authorization process in this format: https://www.integromat.com/oauth/cb/app

oauth.localRedirectUri

Contains redirect URL for OAuth 2.0 authorization process in this format: https://www.make.com/oauth/cb/app or this format: https://www.private-instance.com/oauth/cb/app

oauth.makeRedirectUri

Contains redirect URL for OAuth 2.0 authorization process in this format: https://www.make.com/oauth/cb/app

Example

You can find the OAuth 2 connection example in one of our open-sourced apps, e.g. Smartsheet.

Connections

APIs usually employ some sort of authentication/authorization to limit access to their endpoints.

Make platform provides you with a list of the most used types of connections while in every each there is a code prefilled. You only need to edit it up to your and/or API's needs.

Create a new Connection panel

Mostly, you will need to change the URLs and names of the parameters.

Types of Connections

Common data

When you're using an OAuth connection, an ID and secret are generated for your client. To store them you should use the common data inside the connection.

Make sure nobody else knows the client secret, otherwise your app can get vulnerable.

Once the app becomes Approved, the Common Data gets locked and it can't be changed anymore due to security reasons.

Inside the connection, common data can be accessed by common.variable IML expression.

When you need the common data to be available to all modules, use Apps common data instead.

Common data are stored in encrypted form in Make.

Reserved Words in Connections

Reserved words are variables used internally by Make platform. Using reserved words for the parameter name key can lead to unexpected results. Avoid using a reserved word If you don't have a clear intention of why you want to use it.

Make reserved words are:

  • accountName: name of the connection used by the app module,

  • teamID: ID of the team to which active user is assigned currently.

If you use a Make reserved word for the name key of a parameter, the value stored in the internal Make parameter will be used by your parameter too.

Consider the following configuration of a connection. The parameter labeled Account Name has his name key set to preserved word accountName.

[
    {
        "name": "apiKey",
        "type": "password",
        "label": "API Key",
        "editable": true,
        "required": true
    },
    {
        "name": "accountName", // reserved word used here!
        "type": "text",
        "label": "Account name",
        "editable": true,
        "required": true
    }
]

The setting above leads to mirroring the value from the default Connection name parameter into a parameter labeled Account name. The value accountName is set by Make to the name of the created connection.

Duplication of Connection name's value

Review status

You can check the stage of your custom app review in the App flow tab. The App flow tab contains a list of app review statuses and their description. When you click the App flow tab, your browser automatically scrolls and highlights the current review status of your app.

App Flow tab

If you have published your app or requested a review for a new app or an app update, you have available the Review tab. You can check the Review Status section at the bottom of the Review tab to see all activity that happened during the review.

Review status detail in the Review tab

Every entry contains 4 columns:

  • Date and Time - When the activity happened.

  • Action - Name of the activity.

  • Status - Available only with action "Review status updated.". The status of the app's review.

  • Comment (manual) - A comment with additional information or context.

Review Status

Possible actions are:

Action
Explanation

App has been published.

You have published the app with the Publish button.

Approval requested.

You have submitted the review form. This action marks the start of the app review process.

Review form updated.

You have updated the review form.

Review status updated.

Make updated the review status.

App has been approved.

Make approved your app. You app is now available to every user in Make.

You can check your app review status also in your email. Search for an email with the subject: "App review: YourAppName". Make keeps all communication about the app review in this email thread.

Search modules

Support for query and filtering options

Certain APIs provide support for custom queries or filters when searching records, such as invoices. In Make, our goal is to offer query and filter capabilities to both regular and advanced users.

Therefore, we have implemented two methods of achieving this functionality, and ideally, users should be able to choose between the two options.

A predefined query

We have utilized the familiar filter setup format found in Scenario Builder. With this approach, users are not required to learn the query format. Instead, they can simply set up the query in a manner similar to configuring filters.

When the module is executed, a custom IML function constructs the query, adhering to the specified format.

A custom query

Users have the option to manually compose their own queries. This feature is particularly valuable when the API supports new operators that are not yet available within the module.

To assist users in leveraging the query field effectively, the following helpful information should be provided:

  • Query format: The guidelines for structuring the query.

  • Example of a functional query: An illustrative sample query as reference.

  • API documentation URL: Direct access to the API documentation with query specification.

The operators are organized by category.

Notice the available AND rule.

Notice the help containing the query pattern and the valid query example together with a link to available docs.

Notice the parameter logic that defines the logical relationship between different conditions within a query.

Supported values are AND and/or OR.

Notice that a custom IML function filter is used in order to properly build the query.

The parameters a, o, and b represent the following:

a = name of the parameter, e.g. status

o = operator, e.g. EQ

b = value, e.g. open

Example of the output query:

invoiceNumber~GT~1234|AND|status~EQ~Open

Useful resources

Custom Apps Development Training

Training with 37 lessons and 4.5 hours of video content where you can learn all the aspects of custom apps development on Make.

If you are new to custom apps on Make or want to freshen up your knowledge about apps developing on Make, enroll in the training!

Make's community - Custom Apps

Custom Apps category in community is dedicated to all Makers, especially to those who develop custom apps. If you need help, ask the community! You can participate there as well!

Stack Overflow

100+ questions regarding the custom apps development in Integromat/Make.

You can check if someone already asked the same question and got a response. If not, feel free to post a new question there!

Make's channel full of useful videos:

150+ useful videos about Scenario Builder in Make and many more.

Make's documentation

Documentation about Scenario Builder, apps, and many more.

Regular expressions 101

Recommended tool for experimenting with regular expressions. Just make sure to tick the ECMAScript (JavaScript) FLAVOR in the left panel.

Regular expressions generator

Regular expressions generator for those, who struggle with regex.

Free editor for images

A free editor that you can use to edit your file with a logo to be used in an app.

Make helpdesk

Contact us in case you need help or you didn't find any information you need.

Deprecated: Open source apps

Make will introduce a different way of sharing open-source apps.

Therefore, the Examples tab has been removed and is not available anymore.

Communication
Parameters
Scope
Scope List
Connections
Communication
Parameters
Scope
Scope List
Connections
oauth
[
   {
      "name":"type",
      "label":"Search by",
      "type":"select",
      "required":true,
      "mappable":false,
      "options":[
         {
            "label":"field",
            "value":"filter",
            "nested":[
               {
                  "name":"filter",
                  "label":"Filter",
                  "type":"filter",
                  "grouped":false,
                  "options":{
                     "logic":"and", // --> to support AND only
                     // "logic": "or", --> to support OR only
                     // do not use "logic" parameter --> to support both
                     "store":[
                        {
                           "label":"Status",
                           "value":"Status"
                        },
                        {
                           "label":"Invoice number",
                           "value":"invoiceNumber"
                        }
                     ],
                     "operators":[
                        {
                           "label":"Text",
                           "options":[
                              {
                                 "label":"Contains",
                                 "value":"CTSTR"
                              },
                              {
                                 "label":"Equals",
                                 "value":"EQ"
                              }
                           ]
                        },
                        {
                           "label":"Number",
                           "options":[
                              {
                                 "label":"Less than",
                                 "value":"LT"
                              },
                              {
                                 "label":"More than",
                                 "value":"GT"
                              }
                           ]
                        }
                     ]
                  }
               }
            ]
         },
         {
            "label":"user-defined condition",
            "value":"query",
            "nested":[
               {
                  "name":"query",
                  "label":"Query",
                  "type":"text",
                  "help":"Example: `{FIELD_NAME}~{OPERATOR}~{VALUE}|AND|{FIELD_NAME}~{OPERATOR}~{VALUE}`. E.g. `customer_id~GT~1234|AND|status~EQ~Open`. More examples how to use filters are in [docs](https://myapi.com/how-to-use-filters).",
                  "required":true
               }
            ]
         }
      ]
   },
   {
      "name":"limit",
      "label":"Maximum number of returned invoices",
      "default":10,
      "type":"uinteger"
   }
]
{
    ...
    "qs": {
        "filter": "{{if(parameters.type === 'filter', filter(parameters.filter), parameters.query)}}"
    },
    ...
function filter(filter) {

    if(!filter) return;

    return filter[0].map(and => {
        if(!and.a && !and.o && !and.b) return;
        return `${and.a}~${and.o}~${and.b}`;
    }).join('|AND|');

}
Example of filters
Example of a query
Example of a custom query
Logo
Basic connection
JWT
OAuth 1.0
OAuth 2.0
Course curriculum
Custom apps > tab EXAMPLES

Webhooks

Webhooks power up Instant Triggers, which execute the flow immediately after the remote server sends data.

To use webhooks effectively, you must always create an Instant Trigger and link it to a webhook.

Specification

Specifies how to get data from the payload and how to reply to a remote server.

{
    "verification": {
        "condition": String|Boolean,
        "respond": {
            "type": Enum[json, urlencoded, text],
            "status": String|Number,
            "headers": Object
            "body": String|Object,
        }
    },
    "respond": {
        "type": Enum[json, urlencoded, text],
        "status": String|Number,
        "headers": Object
        "body": String|Object,
    },
    "iterate": {
        "container": String,
        "condition": String|Boolean
    },
    "output": String|Object,
    "condition": String|Boolean,
    "uid": String
}

Note: If the webhook returns multiple items in one batch, you might need to use the iterate directive to specify which items to output. Then you might want to specify the output directive to map items to output. If you do not specify the output directive, items will be returned as-is.

Key

Type

Description

Response Specification

Specifies how to respond to the remote server

Verification Specification

Specifies how to reply to the remote server, if it needs a confirmation

IML String or Iterate Specification

Specifies how response items (in case of multiple) are retrieved and processed.

Any IML Type

Describes structure of the output bundle.

IML String or Boolean

Determines if to execute current request or never.

IML String

Specifies how to get the user ID from the request body.

respond

Required: no

This directive lets you customize Integromat’s response on the webhook or a verification request.

Key
Type
Required
Specification

type

IML String

no

Specifies how to encode data into body.

Default: json.

Available values: json, urlencoded, text.

status

IML String

no

Specifies the HTTP status code that will be returned with the response.

headers

IML Flat Object

no

Specifies custom headers that are to be sent with the response.

body

Any IML Type

no

Specifies the response body.

verification

Required: no

This directive allows you to reply to webhook verification requests. Some systems will not allow you to create webhooks prior to verifying that the remote side (in this case Make) is prepared to handle them. Such systems may send a code and request Make to return it and may be some other value with it. In such case, this directive will help you.

Key

Type

Description

IML String

Specifies how data are serialized into body.

IML String

Specifies the response status code.

Example:

{
    "verification": {
        "condition": "{{if(body.code, true, false)}}",
        "respond": {
            "status": 202,
            "type": "json",
            "body": {
                "code": "{{body.code}} | 123abc"
            }
        }
    }
}

condition

Required: no Default: true

This directive distinguishes normal webhook requests from verification requests. Usually, the remote service will send some kind of code to verify that Integromat is capable of receiving data. In such case, you may want to check for the existence of this code variable in the request body. If it exists - this means that this request is a verification request. Otherwise, it may be a normal webhook request with data.

respond

Required: no

This directive is exactly the same as the respond directive, except that it is nested in verification. The behavior of verification.respond, is the same as normal respond.

iterate

Properties of the iterate directive are described in the Communication docs. Read more.

output

Properties of the output directive are described in the Communication docs. Read more.

condition

Properties of the condition directive are described in the Communication docs. Read more.

uid

Required: only in shared webhooks

Specifies how to get the user ID from the request body. This value is then used to search for the recipient of the message in the database of connections. Don't forget to specify the uid parameter in the connection definition.

Available IML variables

These IML variables are available for you to use everywhere in a webhook:

  • now - Current date and time

  • environment - TBD

  • parameters - Contains webhook’s input parameters.

  • data - Alias for parameters.

  • body - Contains the body of an incoming webhook.

  • query - Contains query string parameters of an incoming webhook.

  • method - Contains HTTP method of an incoming webhook.

  • headers - Contains headers of an incoming webhook.

Types of webhooks

Shared

Shared webhooks come to use when the service sends all the notifications for all the users to only one registered URL.

Dedicated

Unlike a shared webhook, a dedicated webhook is directly linked to the user account. Only notifications for the specific user are received.

Write IML tests

When using IML functions, that work with date and time, remember to set the correct timezone in extension settings. The accepted format is the international timezone format.

For example "Europe/Prague"

Write a test

It's possible to write tests for your custom IML functions. You can use it function and asserts as you may already know them from Mocha and other testing frameworks. Our example function has the following code:

function exampleFunction(number) {
       return number * 2
       }
Example function

So, let's write a test for this function. We'll create two blocks.

it('First test', () => {
    assert.ok(exampleFunction(2) === 4)
    })
it('Second test', () => {
    assert.ok(exampleFunction(2) === 5)
    })
Example test with 2 blocks

As you can see, the it function accepts exactly two parameters, the name of the test and the code to run. In this code, we can verify expected outputs using assert.ok() function.

Run a test

To run a test on a specific function, right-click the function name in the tree and select the Run IML test option.

Run IML test option

The test will start and you'll see the output in the IML tests output channel.

Output from the IML test

If you need to debug your custom IML function, follow the article below.

Make DevTool

Make DevTool allows you to debug your Make scenarios by adding an extra pane to the Chrome Developer Tools. Using this new debugger pane, you can check all the manual runs of your scenario, review all the performed operations and see the details of every API call performed. Additionally, using Make DevTools you can see which module, operation, even which single response causes an error in your scenario. This helps you debug your scenario, and get your scenario back on track.

DevTool in action

To get started, just install the extension from the Chrome Webstore.

DevTool consists of 3 main modules - Live Stream, Scenario Debugger, and Tools. Each of them is described in detail in the next subpages.

DevTool's options

REST

Naming Convention

As we want to keep the naming of the Universal Modules the same across all Apps which support them, the Universal Module should keep the following guidelines.

Module label: Make an API Call

Module description: Performs an arbitrary authorized API call.

URL Parameter

Expected input from users should start with / (for example /tasks) so users can copy-paste the endpoint path from the service documentation.

If the API has multiple versions of API available, the user should be allowed to use any of them. The URL set in the Universal module should end before the version.

Set the correct URL in the "help" and add a working endpoint example.

Communication

Mappable parameters

Even when the URL in Communication ends with / before {{parameters.url}} we ask users to use / in the URL because it is automatically removed.

Communication

Mappable parameters

The "url" in the Communication has API version in it.

The "help" has misleading example, the base url should end without slash and version and the example start with slash and version.

Example:

OAuth Scopes

When your app requires specifying scopes to access different groups of endpoints, you need to tweak the connection code a bit to make it work correctly with the Universal Module. Here's how:

Step 1

Add a new advanced parameter called scopes to the connection parameters.

Step 2

In the authorize part of the connection, merge the original scopes with additional scopes added by the parameter from the previous step.

Step 3

Now when you want to use Universal Module with scopes that have not been granted to the connection previously, you can create a new connection and request those additional scopes manually.

Shared
Dedicated
respond
verification
iterate
output
condition
uid
condition
respond
{
    "url": "https://www.example.com/{{parameters.url}}"
}
{
        "name": "url",
        "type": "text",
        "label": "URL",
        "help": "Enter a path relative to `https://www.example.com`. For example: `/v1/something`",
        "required": true
}
{
    "url": "https://www.example.com/v1/{{parameters.url}}"
}
{
        "name": "url",
        "type": "text",
        "label": "URL",
        "help": "Enter a path relative to `https://www.example.com/v1/`. For example: `something`",
        "required": true
}
{
	"qs": {
		"{{...}}": "{{toCollection(parameters.qs, 'key', 'value')}}"
	},
	"url": "https://www.example.com/{{parameters.url}}",
	"body": "{{parameters.body}}",
	"type": "text",
	"method": "{{parameters.method}}",
	"headers": {
		"{{...}}": "{{toCollection(parameters.headers, 'key', 'value')}}"
	},
	"response": {
		"output": {
			"body": "{{body}}",
			"headers": "{{headers}}",
			"statusCode": "{{statusCode}}"
		}
	}
}
[]
[
	{
		"help": "Enter a path relative to `https://www.example.com`.\nFor example: `/v1/api/list`",
		"name": "url",
		"type": "text",
		"label": "URL",
		"required": true
	},
	{
		"name": "method",
		"type": "select",
		"label": "Method",
		"default": "GET",
		"options": [
			{
				"label": "GET",
				"value": "GET"
			},
			{
				"label": "POST",
				"value": "POST"
			},
			{
				"label": "PUT",
				"value": "PUT"
			},
			{
				"label": "PATCH",
				"value": "PATCH"
			},
			{
				"label": "DELETE",
				"value": "DELETE"
			}
		],
		"required": true
	},
	{
		"help": "You don't have to add authorization headers; we already did that for you.",
		"name": "headers",
		"spec": [
			{
				"name": "key",
				"type": "text",
				"label": "Key"
			},
			{
				"name": "value",
				"type": "text",
				"label": "Value"
			}
		],
		"type": "array",
		"label": "Headers",
		"default": [
			{
				"key": "Content-Type",
				"value": "application/json"
			}
		]
	},
	{
		"name": "qs",
		"spec": [
			{
				"name": "key",
				"type": "text",
				"label": "Key"
			},
			{
				"name": "value",
				"type": "text",
				"label": "Value"
			}
		],
		"type": "array",
		"label": "Query String"
	},
	{
		"name": "body",
		"type": "any",
		"label": "Body"
	}
]
[
	{
		"name": "body",
		"type": "any",
		"label": "Body"
	},
	{
		"name": "headers",
		"type": "collection",
		"label": "Headers"
	},
	{
		"name": "statusCode",
		"type": "number",
		"label": "Status code"
	}
]
{}
[
    {
        "name": "clientId",
        "type": "text",
        "label": "Client ID",
        "advanced": true
    },
    {
        "name": "clientSecret",
        "type": "text",
        "label": "Client Secret",
        "advanced": true
    },
    {
        "name": "scopes",
        "label": "Additional Scopes",
        "type": "array",
        "help": "Use this to get an access to extra scopes.",
        "advanced": true
    }
]
"authorize": {
        "qs": {
            "scope": "{{join(distinct(merge(oauth.scope, ifempty(parameters.scopes, emptyarray))), ',')}}",
            "client_id": "{{common.clientId}}",
            "redirect_uri": "{{oauth.redirectUri}}",
            "response_type": "code"
        },
        "url": "...",
        "response": {
            "temp": {
                "code": "{{query.code}}"
            }
        }
}
Example of creation of a new universal module for Make app
Example of universal module
Advanced Parameter Scopes
Scope Parameter in Authorization
Example of connection with additional scopes
Debug IML in VS Code
Handling Responses

Trigger (polling)

The Trigger module is a special module that saves the information about what was the last item processed and continues the execution from that item, if there is some.

You can configure the trigger module to:

  • Process all available items and wait for new ones, without repeated processing of the old item.

  • Process items starting from a specific date and time.

  • Process items starting with a specific item.

Use this module when you need to process items sequentially in the order they were created/updated.

Components

Communication

  • Communication response is extended with the trigger object.

response.trigger

The trigger collection specifies directives that will control how the trigger will work and how your data will be processed

Key

Type

Description

date or id

Specifies how the trigger will behave and sort items

asc or desc

Specifies in what order the remote API returns items

IML String

Must return current item’s Id

IML String

When used, must return current item’s date

response.trigger.type

Required: yes Values: id or date

The trigger.type directive specifies how the trigger will sort and iterate through items.

If the processed item has a create/update date, then date should be used as a value, and a correct getter should be specified in trigger.date directive. The trigger will then sort all items by their date and id fields and return only unprocessed items.

If the processed item does not have a create/update date, but only an id, then id should be used as a value, and a correct getter should be specified in trigger.id directive.

response.trigger.order

Required: yes Values: asc, desc or unordered

The trigger.order directive specifies in what order the remote API is returning items - descending, ascending, or unordered. This information is needed to correctly determine if there are more pages to be fetched or not. It is also needed to correctly sort the incoming items and display them to the user in ascending order.

So if the API is returning items in ascending order (low to high), then asc should be used. If the API is returning items in descending order (high to low), then desc should be used. If the API is returning items in no apparent order, then unordered should be used.

When specifying the Trigger's communication, you should sort the results in descendant order, if possible, as the module won't have to page all over the old records to get the new ones.

response.trigger.id

Required: yes

This directive specifies the item’s id. It must always be present. For example, if your item looks like this:

{
    "id": 24,
    "name": "Fred",
    "friend_count": 5
}

then you should specify your trigger.id directive like this: {{item.id}}:

{
    "response": {
        "trigger": {
            "id": "{{item.id}}"
        }
    }
}

response.trigger.date

Required: yes, if the trigger type is date

This directive specifies the item’s date. It must be specified when the trigger.type is set to date. Note that trigger.id must always be specified.

For example, if your item looks like this:

{
    "id": 24,
    "name": "Fred",
    "friend_count": 5,
    "created_date": "2017-07-05T13:05"
}

Then you should specify your trigger.date directive like this: {{item.created_date}}, and your trigger collection might look something like this:

{
    "response": {
        "trigger": {
            "id": "{{item.id}}",
            "date": "{{item.created_date}}"
        }
    }
}

Epoch

The Epoch panel is a specific component of the trigger allowing a user to choose the starting item.

Static Parameters

The Trigger module can only have static parameters. There's no reason to have anything mappable in the Trigger as this module is always the first module in the scenario.

Interface

The Trigger module can return multiple bundles at once.

​Samples​

To help the users with setting up your module, you can provide samples to it.

​Scope​

When using an OAuth type of connection, use the Scope to define scopes required by this trigger.

Available IML Variables

These IML variables are available for you to use everywhere in this module:

  • now - Current date and time.

  • environment - TBD

  • temp - Contains custom variables created via temp directive.

  • parameters - Contains module’s input parameters.

  • connection - Contains connection’s data collection.

  • common - Contains app’s common data collection.

  • data - Contains module’s data collection.

    • data.lastDate - Returns the date from the last retrieved item in a previous execution.

    • data.lastId - Returns the ID of the last retrieved item in a previous execution.

  • scenario - TBD

  • metadata.expect - Contains module’s raw parameters array the way you have specified it in the configuration.

  • metadata.interface - Contains module’s raw interface array the way you have specified it in the configuration.

Additional variables available to Response Object:

  • output - When using the wrapper directive, the output variable represents the result of the outputdirective.

Additional variables available after using the iterate directive, i.e. in wrapper or pagination directives:

  • iterate.container.first - Represents the first item of the array you iterated.

  • iterate.container.last - Represents the last item of the array you iterated.

In the Trigger module, the iterate.container.last can be used for handling the pagination of the new items correctly.

"response": {
    "limit": "{{parameters.limit}}",
    "output": "{{parseItem(item.data)}}",
    "iterate": "{{body.data.children}}",
    "trigger": {
        "id": "{{item.data.name}}",
        "date": "{{parseDate(item.data.created_utc, 'X')}}",
        "type": "date",
        "order": "desc"
    }
},
"pagination": {
    "qs": {
        "after": "{{iterate.container.last.data.name}}"
    }
}

Additional variables available to pagination and response objects:

  • body - Contains the body that was retrieved from the last request.

  • headers - Contains the response headers that were retrieved from the last request.

  • item - When iterating this variable represents the current item that is being iterated.

Example

Example of a trigger
{
	"qs": {},
	"url": "/api/users",
	"body": {},
	"method": "GET",
	"headers": {},
	"response": {
		"limit": "{{parameters.limit}}",
		"output": {
			"id": "{{item.id}}",
			"name": "{{item.name}}",
			"email": "{{item.email}}",
			"created": "{{item.created}}"
		},
		"iterate": "{{body.users}}",
		"trigger": {
			"id": "{{item.id}}",
			"date": "{{item.created}}",
			"type": "date",
			"order": "desc"
		}
	}
}
{
	"response": {
		"limit": 500,
		"output": {
			"date": "{{item.created}}",
			"label": "{{item.name}}"
		}
	}
}
[
	{
		"help": "Maximum number of results Integromat will work with during one execution cycle.",
		"name": "limit",
		"type": "uinteger",
		"label": "Limit",
		"default": 2,
		"required": true
	}
]
[
	{
		"name": "id",
		"type": "uinteger",
		"label": "User ID"
	},
	{
		"name": "email",
		"type": "email",
		"label": "Email address"
	},
	{
		"name": "name",
		"type": "text",
		"label": "Name"
	},
	{
		"name": "created",
		"type": "date",
		"label": "Date created"
	}
]
{
	"id": 1,
	"email": "[email protected]",
	"name": "John Doe",
	"created": "2018-01-01T12:00:00.000Z"
}

Request app review

Once you make sure that your app meets Make's requirements and follows Make's best practices, you can request an app review:

Once you publish your app in the second step of the following procedure, you can't "unpublish" it. Check the app visibility section for more details.

  1. Make sure that you removed testing modules or testing connections before you publish an app. When your app is public, you can't delete its modules, connections, or other components.

  2. Navigate to your custom app's code and click the Publish button:

  3. Navigate to the Modules tab. Click the switch to set the visible status of each module of the app that you want to publish.

  4. After the first step, a new tab Review appeared in the top panel. Navigate to the Review tab:

    The Review tab
  5. Fill in the form in the Review tab.

    • Add a link to the API documentation of the service for which you are creating a custom app.

    • Add a link to scenarios with the custom app modules. Check the testing scenarios section in the App review prerequisites.

  6. Click the Request review button at the bottom of the page.

The data you filled in the form are sent to Make QA developers for review. At the top of the page, the Request review button changes to Review process started and becomes inactive. Also, the app status turns into pending approval.

"Review process started" status

Once you request the app review, Make sends you an e-mail with the subject: "App review: YourApp'sName". If you have any questions or additional information to share, reply to the e-mail.

Each time you update your app based on reviewer feedback, ensure you run your scenarios to generate new logs.

If you need to edit your links, you can update your form and submit the changes by clicking on Update review button.

Review process

The review process contains 3 phases - form submission, automatic review and manual review.

Form submission

Once you hit the Request Review button, you will receive an e-mail containing a link to our form, where you will be prompted to share the following information about you and your app, that will help us to provide better service to you:

  • Developer information - What is your relationship with the vendor of the API service.

  • Partnership contact - Applicable for ISV. The details of a contact person or team on your end responsible for partnership-related discussions.

  • Support contact - Details of the contact person or team that should be reached out to in case of any app issues reported by users.

  • Categories and subcategories of your app - The categories and subcategories in which you wish to have the app listed on make.com integrations page.

  • Logo of your company - Applicable for ISV. The logo should appear on the app's landing page on make.com.

  • URL to the service - URL leading to your website that should appear on the app's landing page on make.com.

Upon completing the form, you will receive feedback from the automatic review.

Automatic review (beta)

Your app is first reviewed by our application. The automatic review checks the common issues and generates a PDF file with the issues found.

The PDF file with feedback from the automatic review is sent to the existing App review email thread.

The automatic review is in beta and is still in the process of improvement. Therefore, there might be inconsistencies.

Manual review

Once your app passes the automatic review, it is reviewed by our QA Engineer who makes sure, that your app follows our best practices and is user-friendly.

QA Engineer shares the feedback in the existing App review email thread.

Once your app successfully passes the manual review, your app is listed in our planned release. You will be informed about the app approval as well as the app release.

App visibility

Private app

A private app can only be used by the author of the app until the app is installed in an organization to which the author and other users have access.

If you want to make your app available to everyone in your organization, create a new scenario with your app and save the scenario there.

A dialog about the confirmation of installation of your app in your organization will pop up. Confirm the installation.

Confirmation dialog about app's installation in an organization

A private tag is displayed after your app's name on the app's page.

"Private" tag

Deletion of a private app

If your private app is not used in any of your scenarios, you can delete the private app.

Deletion of a module in a private app

You can delete a module only if it is not used in any scenario. If you will try to do so, a warning dialog with a request to remove the module from a scenario first will pop up.

Deletion dialog

Deletion of a component in a private app (webhook, connection, RPC, custom IML function)

You can delete a component only if it is not used in any module. If you will try to do so, a warning dialog in the right-down corner will pop up.

Warning a connection is used by a module

Public app

If you want to share your app outside of your organization, you will need to publish your app, so the invite link is generated.

Go to your app and open your app's page. Click Publish in the right upper corner.

Publishing of private app

Once you publish the app, the invite link is generated. You can share it with any other Maker who will then install it in his/her organization. It doesn't matter, where the organization is located (EU1 or US1).

Invite link

Deletion of a public app

Once the app is published, it is not possible to delete the app or make it private again.

If you wish to have your public app deleted, contact our helpdesk and we will remove the app from your account.

Deletion of a module or other component in a public app (webhook, connection, RPC, custom IML function)

Once the app is published, it is not possible to delete any module or component.

We recommend you change the component's label the way so that it is obvious the component should not be used, e.g. [DO NOT USE] My unwanted module. Make sure the module is hidden.

Distribution control of modules in a public app

If you actively share your public app via an invite link, you can control the distribution of modules that you have in your public app by making them hidden/visible.

Switching toggles to make an app either visible or hidden

If you hide an already visible module, that is currently in use in a scenario, the module will continue working in the scenario. Though, the hidden module will not be available in the app selector in a new scenario.

Approved app (=globally available app)

If you want to have your app available to all Makers, you will need to first make sure your app follows our conditions and best practices.

Once you make sure your app follows our conditions and best practices, click Request review button.

Request review button

Once the app passes our review process, it becomes available for everyone to use in our App Builder. That mean's that any user on Make can add your app to his/her scenario and use it.

Communication
Epoch
Static parameters
Interface
Samples
Scope
type
order
id
date
App review prerequisites
Best practices

Processing of input parameters

Mapping all parameters

Notice, that you have to map every single parameter correctly.

There is a risk of a typo or omission of a parameter.

Thanks to this approach, all parameters from the modules will be sent to the API.

[
    {
        "name": "email",
        "type": "email",
        "label": "Email address",
        "required": true
    },
    {
        "name": "firstName",
        "type": "text",
        "label": "First Name",
        "required": true
    },
    {
        "name": "lastName",
        "type": "text",
        "label": "Last Name",
        "required": true
    }
]

Using an IML function or omitting a parameter

Sometimes, you don't want to map all parameters in the body for some reason:

  • the parameter shouldn't be sent at all (technical parameters such as selects, etc.)

  • the parameter should be sent somewhere else than in the body, e.g. in the url

  • the parameter has to be wrapped in an IML or custom IML function

{
    "url": "/contact/{{parameters.id}}",
    "method": "PUT",
    "body": {
        "email": "{{parameters.email}}",
        "firstName": "{{parameters.firstName}}",
        "lastName": "{{parameters.lastName}}",
        "registrationDate": "{{formatDate(parameters.registrationDate, 'YYYY-MM-DD')}}"
        
    },
    // ...
}

Notice, that you have to map every single parameter correctly.

There is a risk of a typo or omission of a parameter.

{
    "url": "/contact/{{parameters.id}}",
    "method": "PUT",
    "body": {
        "{{...}}": "{{omit(parameters, 'id', 'registrationDate')}}",
        "registrationDate": "{{formatDate(parameters.registrationDate, 'YYYY-MM-DD')}}"
    },
    // ...
}

Notice, that "{{...}}" lists all available parameters from the module and allows adding other parameters.

The omit() function allows the removal of parameters, that are used somewhere else, shouldn't be used, or require special attention.

In this case, the id parameter is already used in the url, and the registrationDate parameter is wrapped in formatDate IML function.

[
    {
        "name": "id",
        "type": "string",
        "label": "Contact ID",
        "required": true
    },
    {
        "name": "email",
        "type": "email",
        "label": "Email address"
    },
    {
        "name": "firstName",
        "type": "text",
        "label": "First Name"
    },
    {
        "name": "lastName",
        "type": "text",
        "label": "Last Name"
    },
    {
        "name": "registrationDate",
        "type": "date",
        "label": "Date of Registration"
    }
]

Handling arrays, nulls, and empty strings

Make and other third-party services transport values in many different formats. It is important to know how to handle the value types of arrays, nulls, and empty strings.

Bad practices

{
    "url": "/messages.json",
    "method": "POST",
    "body": {
        "status": "active",
        "content": "{{ifempty(parameters.content, undefined)}}"
    },
    // ...
}

It isn't possible to send a null value to a service.

{
    "url": "/messages.json",
    "method": "POST",
    "body": {
        "status": "active",
        "content": "{{if(parameters.content, parameters.content, undefined)}}"
    },
    // ...
}

It isn't possible to send a null, empty string, and 0 (zero) value to a service.

{
    "url": "/tasks.json",
    "method": "POST",
    "body": {
        "status": "active",
        "tags": "{{if(length(parameters.tags) > 0, parameters.tags, undefined)}}"
    },
    // ...
}

It isn't possible to send empty array to a service. E.g. User wants to remove all tags from a task.

Let users decide what parameters they want to send to the service. Make has a feature to show how to process values. This feature allows users to define exactly how Make should behave when the value is "empty". For example, if a user defines that they want to send a specific field to a service even if the value is null , empty string, or an empty array, it will be sent to the service. In module communication, config passes parameters without any modification.

Date parameters

When a field is a type "date", it should be possible to use our keyword "now" as a value, which means, the field should accept ISO-8601 date format and if the service requires only the date (no time) or a different format like timestamp, this formatting should happen inside the module.

Users of the app should never be prompted to format the date inputs the way API requires! Such apps will not be approved by Make.

Communication:

{
    "url": "/api/users",
    "method": "POST",
    "body": {
        "name": "{{parameters.name}}",
        "birthday": "{{formatDate(parameters.birthday, 'YYYY-MM-DD')}}",
        "due_date": "{{formatDate(parameters.due_date, 'X')}}"
    },
    "response": {
        "output": "{{body}}"
    }
}

Parameter birthday is required to have format YYYY-MM-DD and parameter due_date is required to be a timestamp by the service, so the formatting happens inside the Communication part of the module.

Parameters:

[
    {
        "name": "name",
        "type": "text",
        "label": "Name"
    },
    {
        "name": "birthday",
        "type": "date",
        "label": "Birthday"
    },
    {
        "name": "due_date",
        "type": "date",
        "label": "Due Date"
    }
]

The parameters birthday and due_date are correctly date typed and don't need to be formatted by the user, he can freely work with now keyword.

Communication:

{
    "url": "/api/users",
    "method": "POST",
    "body": {
        "name": "{{parameters.name}}",
        "birthday": "{{parameters.birthday}}",
        "due_date": "{{parameters.due_date}}"
    },
    "response": {
        "output": "{{body}}"
    }
}

Parameter birthday is required to have format YYYY-MM-DD and parameter due_date is required to be a timestamp but nothing is done with the value.

Parameters:

[
    {
        "name": "name",
        "type": "text",
        "label": "Name"
    },
    {
        "name": "birthday",
        "type": "date",
        "label": "Birthday",
        "help": "Format YYYY-MM-DD"
    },
    {
        "name": "due_date",
        "type": "integer",
        "label": "Due Date",
        "help": "Enter a timestamp"
    }
]

The parameter due_date is an incorrect type and birthday is required to be formated by the user.

Query String (QS)

Query string parameters should be defined using the qs directive as a key-value collection, where the key is the parameter name and the value is the parameter value. Values in the qs collection are automatically encoded, so you do not need to escape them manually.

Note: The same automatic encoding applies to the headers and body collections for request headers and body payloads, respectively.

Example:

{
    "url": "http://yourservice.com/api/items",
    "qs": {
        "limit": 100,
        "since": "{{parameters.since}}",
        "until": "{{parameters.until}}"
    }
    // ... other directives
}

This will issue the request to URL like:

http://yourservice.com/api/items?limit=100&since=2023-01-01&until=2023-01-31

Note: If you provide a query string directly in the url directive, it will not be automatically encoded. You have to encode it manually. But in common cases, the entering query string in url is not recommended approach, especially when values are inserted dynamically. See below section for the details.

Special case: Query string parameters in the URL

In most cases, the query string in URL is not the best practice. It is better to use the qs collection, but sometimes there is a special usecase when you need to use the query string directly in the URL.

The main difference is that the query string in the URL is not encoded automatically, so you have to do it manually. This can be useful when the 3rd-party service requires a very specific format or encoding of the query string parameters. In that case you can add query string directly to the url directive string and manage the encoding yourself.

Example of specific query string parameters in the URL:

{
    "url": "http://yourservice.com/api/users/{{parameters.userId}}?groups=(1,2,3,4)&specificallyEncodedParameter={{parameters.specific}}",
    // qs: {} // <- Do not include the `qs` collection here
    "method": "POST",
    // ...
}

The most common use case for this is when 3rd-party service requires special symbols like brackets [] or parentheses () to be unencoded in the query string.

Important: Never mix the qs directive together with the query string in the URL. This will lead to invalid escaping of parameters specified in the url.

Using arrays in qs

You can also use an array as a value in the qs collection. In this case, the resulting query string will repeat the key for each value in the array, e.g. ?tag=one&tag=two&tag=three.

Example:

{
    "url": "http://yourservice.com/api/items",
    "qs": {
        "anytag": ["one", "two", "three"]
    }
}

This will issue the request to URL like:

http://yourservice.com/api/items?anytag=one&anytag=two&anytag=three

Using structured data in qs

NOTE: qs (and also headers) are single-level collections only, meaning that you cannot specify nested collection in their parameters:

Incorrect structure example:

{
    "qs": {
        "someProp": {
            "anotherOne": { // <- Collection in Collection is NOT allowed like this
                "and-one-more": "THIS WILL NOT WORK"
            }
        }
    }
}

The example above will not work.

Because there is no defined standard for how to encode nested objects in query string, you need to implement it manually based on special requirements of 3rd party service. There can be couple of possible approaches, but the most common one is to use dot notation for nested properties. This means that you can use a dot . to separate the keys in the query string.

Example of dot notation:

Correct structure example:

{
    "qs": {
        "someProp.anotherOne.and-one-more": "THIS WILL WORK"
    }
}

This will issue the request with the query string:

?someProp.anotherOne.and-one-more=THIS%20WILL%20WORK
Make (formerly Integromat)YouTube
regex101: build, test, and debug regexregex101
Specialty Badge - Custom Apps Development TrainingMake Partners
Contact support |
Custom AppsMake Community
LunaPic.com Photo Editor toolLunapic
Regex Generator - Creating regex is easy again!

Basic connection

Connection is a link between Make and 3rd party service/app.

Basic type of connection is a group of connections, which can use:

  • API Key,

  • Basic Auth (a pair of username and password base64 encoded),

  • Digest Auth (a pair of credentials md5 hashed).

Basic Connection can also be used, when API doesn't support OAuth 1/2.

Components

Communication

  • aws directive is not available

  • Only a single request can be performed

  • pagination directive is not available

  • response.limit is not available

  • response.iterate directive is not available

  • response.output is not available

  • response is extended with data , uid and metadata

response.data

The data directive saves data to the connection so that it can be later accessed from a module through the connection variable. It functions similarly to the temp directive, except that data is persisted in the connection.

Example:

{
    "response": {
        "data": {
            "accessToken": "{{body.token}}"
        }
    }
}

This accessToken can be later accessed in any module that uses this connection like so:

{
    "url": "http://example.com",
    "qs": {
        "token": "{{connection.accessToken}}"
    }
}

response.metadata

The metadata directive allows you to save the user’s name or username (or any other text field) so that multiple connections of the same type could be easily recognized. A common practice is to save either username or email or full name to metadata.

The metadata object has 2 properties: value and type. value is used to store the value and type is used to specify what the value is. Currently, there are only 2 types: email and text.

Example:

Example of connection's name
...
"response": {
	"metadata": {
            "type": "email",
            "value": "{{body.data.user.email}}"
        }
	},
...

response.uid

This directive allows you to save the user’s remote service Id. This is required when using Shared Webhooks.

Example:

{
    "response": {
        "uid": "{{body.data.id}}"
    }
}

Parameters

Parameters that the user should fill while creating a new connection.

Common Data

Non-user-specific sensitive values like salts or secrets.

Available IML variables

These IML variables are available for you to use everywhere in this module:

  • now - Current date and time.

  • environment - TBD

  • temp - Contains custom variables created via temp directive.

  • parameters - Contains the connection’s input parameters.

  • common - Contains connection’s common data collection.

  • data - Contains connection's data collection.

  • oauth.scope - Contains an array of scope required to be passed to OAuth 2.0 authorization process.

  • oauth.redirectUri - Contains redirect URL for OAuth 2.0 authorization process.

Example

Here you can see an example of API key-based connection.

{
	"url": "https://sample.com/api/v1",
	"method": "GET",
	"headers": {
		"authorization": "Basic {{base64(parameters.apiKey + ':' + connection.domain)}}"
	},
	"response": {
		"error": {
			"type": "{{body.code}}",
			"message": "{{body.message}}"
		},
		"valid": "{{if(body.code, false, true)}}"
	},
	"log": {
		"sanitize": ["request.headers.authorization"]
	}
}
Communication
Parameters
Connections
Interface Generator
{
    ...
    "body": {
        "firstName": "{{parameters.firstName}}",
        "lastName": "{{parameters.lastName}}",
        "email": "{{parameters.email}}"
    },
    // ... other directives
}
{
    // ...
    "body": "{{parameters}}",
    // ...
}
Make Help center
Logo

Module

Modules are the key component of your app. They are basically wrappers around specific app/service functionality, which is exposed via an API endpoint.

There are three basic types of modules: Action, Search, and Trigger (polling).

Make works with 6 types of modules, read more about them .

Setting up a new module for our demo API

Creation of a new module

To create a new Module, click on the tab MODULES. The list of all modules your app consists of, will be shown (empty for now). Click the large button with the plus sign and choose Create a new Module.

A dialog will pop up, where you can name your module, choose its type, and provide some description. Fill the dialog as shown and click Save.

Make sure the tab Communication is active and replace the content of the text area with the following JSON and save the changes (Ctrl+S):

The url key specifies the API endpoint path. The URL will be joined with baseUrl specified earlier to produce the full URL of the API endpoint: http://demo-api.integrokit.com/api/v1/helloworld.

In order to use baseUrl specified in the base, the URL should start with /. If the URL doesn't start with /, the baseUrl doesn't come into use, and only the content of url is used instead.

Click the tab Mappable parameters. The JSON on this tab enables you to specify the parameters of your module that will appear in the . Our module does not require any parameters, so erase the content between the square brackets, leaving just empty square brackets and save the changes (Ctrl+S):

Congratulations! You have just created your first module.

Testing the new module

Now, we will test our new module. Open a new browser tab, login to Make, in the left main menu choose Scenarios and create a new scenario. Click the yet undefined “questionnaire” module to bring up a list of all the apps. Search for your new app by typing its name in the Search field: Custom App. Click your app and a list of all its modules will be shown, currently just the newly created Hello World module. Click the module to select it. An empty module settings panel will pop up saying “There are no configurable options for this module.”.

Close the panel and run the scenario. Click the bubbles above the module to pop up the panel with information about processed bundles. In case you have successfully followed this tutorial, you should see the following output of your new module.

Perfect! You just learned how to create a new module and make it work. But we can make it better! We can make it more user friendly. Continue reading below.

Setting up interface of the module

Since we now know the structure of the output, we can specify the Interface. So we can set up our scenario right away, with no need to first execute the module to learn the output's structure.

Below, you can see an example of a list of available parameters to map from Shopify > Watch Orders module. Notice that the Shopify module wasn't executed yet (the bubble is not displayed).

In the panel with the output, click on the button circled below and choose Download output bundles option.

Then, a new panel will appear, with the original response from the endpoint. Copy the text to your clipboard.

Go back to the tab with your app and make sure you are in the settings of the Hello World module. Select tab INTERFACE. You can see a JSON snippet:

In the right upper corner, click on Options button and choose Generator.

A new panel will appear. There, paste the previously copied JSON from your clipboard and click Generate.

A new data structure will be generated. Copy it to your clipboard and close the panel.

In the INTERFACE, replace the JSON structure with the new structure.

It is recommended to add parameter "label". This label is then visible in the list of parameters available to map from preceding modules.

Whenever you change INTERFACE in a module, you need to refresh your scenario in order to see the changes.

Perfect! You just learned, how to work with interface and automatically prepare a list of expected parameters from the module's output. Now, let's make it harder!

Adding mappable parameters

In Make modules, you can define, what data should be passed to the API. So we can create a new record or define, what should be returned.

The Demo API endpoint /helloworld takes two parameters greeting and name. Click the following link to open the API response in your browser:

The API should return the following JSON response:

So let’s make our Hello World module configurable by adding the parameter greeting. Switch back to the Mappable parameters tab, replace the empty square brackets with the following JSON and save the changes(Ctrl+S):

This JSON specifies that the module will have one parameter called Greeting of type text. More about parameters can be found in the documentation.

Switch to your scenario and refresh the browser window (F5). Click the Module to pop up its settings panel. The panel now contains one text field labeled Greeting. Fill Hi:

Whenever you change MAPPABLE PARAMETERS in a module, you need to refresh your scenario in order to see the changes.

Press OK and run the scenario. Though, if you click the bubbles above the module to pop up the panel with information about processed bundles, the module’s output will be identical as in the previous run.

You can also use to check the original REQUEST and RESPONSE, for further debugging. Learn more about debugging your app .

This is how the log from the run looks like, focus on the Request Headers tab:

Notice, that there is no parameter called greetings sent.

It is because we need to specify the structure of the request in the module. Specifically, we have to pass the content of the greeting parameter to the API.

Switch back to the MAPPABLE PARAMETERS tab and click the COMMUNICATION tab. To pass the parameter greetings to the API, you have two options:

You can either add the parameter to the url key:

Or you can add a new key qs (query string) and add the parameter there:

Save the changes (Ctrl+S), switch to your scenario, and run the scenario. Click the bubbles above the module to pop up the panel with information about processed bundles, where you should see the modified module’s output:

Also, you can check how clear the run in DevTool looks. Finally, the parameter greeting is passed in QS (query string):

You can now practice your skills and try to add a parameter name to your Hello World module.

Congratulations! You have just learned how to pass data from a module to an API. Also, you have learned how you can debug possible issues using our Make DevTool. If you are ready to learn about setting up connection in your app, continue below.

Action

Use if the API endpoint returns a single response. Examples are Create a book, Delete a book or Get a Book.

Search

Use if the API endpoint returns multiple items. An example is List Books that will find specific books according to search criteria.

Trigger (polling)

Use if you wish to watch for any changes in your app/service. Examples are Watch a New Book, which will be triggered whenever a new book has been added to the library.

{
    "url": "/helloworld"
}
[]
[
    {
        "name": "id",
        "type": "uinteger",
        "label": "User ID"
    }
]
[
    {
        "name": "id",
        "type": "uinteger",
        "label": "User ID"
    }
]
[
  {
    "name": "result",
    "type": "text",
    "label": "Result"
  }
]
{"result":"Hi, Johny!"}
[
    {
        "name": "greeting",
        "type": "text",
        "label": "Greeting"
    }
]
{
    "url": "/helloworld?greeting={{parameters.greeting}}"
}
{
    "url": "/helloworld",
    "qs" : {
        "greeting": "{{parameters.greeting}}"
    }
}
here
module settings panel
http://demo-api.integrokit.com/api/v1/helloworld?greeting=Hi&name=Johny
Parameters
Make DevTool
here
Empty List of Modules
Create a New Module DialogmThe new module will appear in the list. Click on the new module and a page with several tabs will be shown.
Configuration of "Hello World" Module
Module's Output
Example Dialog with Available Parameters from Shopify Module
Download Output Bundles Option
Original Response from /helloworld Endpoint.
Interface of the Hello World Module
Generator Panel
A New Data Structure
List of Available Parameters to Map Before
List of Available Parameters to Map After
New mappable field "Greeting"
Live Stream from Make DevTool
Output from the Hello World module
Live Stream from Make DevTool
Newest 'integromat' QuestionsStack Overflow
Logo
Logo
Logo

Managing breaking changes

Breaking change

When you make changes in an app, you need to make sure they will not break existing scenarios. Please, check the below list of common breaking changes.

App's Element or Block
Breaking Changes

Base

Any changes might break scenarios.

Connection

Changing refresh call for OAuth connection.

Module's Communication

Changing response.output.

Changing response.type.

Adding response.valid for 2xx response.

Changing response.trigger.

Adding condition.

Adding additional call (chaining request).

Changing linked connection.

Module's Parameters

Changing required from false to true.

Removing a parameter.

Changing type (if the original type can be coerced to the new type, it’s fine. e.g. number -> text. See .

Adding validate.

Setting select parameter mappable to false.

Setting select parameter dynamic to false.

Webhook's Communication

Any changes might break scenarios.

RPC

Changing parameter required from false to true.

Changing RPCs building dynamic parameters.

Custom Functions

Any changes might break scenarios.

How to remove a parameter from a module

Ideally, the removal of the field/s should be announced to the users in advance.

Please, contact us via the helpdesk with a request for e-mail notification for users, that use the modules in question.

It's important to avoid removing mappable parameters from a module without a clear indication or notification to the user. Even if it doesn't immediately cause a scenario to fail, it could still impact its functionality or disrupt the underlying process. Therefore, it's best to communicate any changes to the user. Also, the user should be able to see and work with the original input in the deprecated parameter/s.

In situations where a mappable parameter needs to be removed, there are several ways to handle the deprecation. The appropriate approach depends on the specific circumstances and how the API manages parameter deprecation in its endpoint. Below, are methods ranked in order of least to greatest impact.

1. Adding [Deprecated] string into the module's label

The parameter should be put into advanced parameters and the [Deprecated] string should be attached to the label. Additionally, you can add instructions to the help.

Example of deprecation of a parameter
[
    {
        "name": "firstName",
        "type": "text",
        "label": "First Name",
	"help":  "This field is a replacement for the deprecated `Name` field."
    },
	{
        "name": "lastName",
        "type": "text",
        "label": "Last Name",
	"help":  "This field is a replacement for the deprecated `Name` field."
    }, 
    {
        "name": "email",
        "type": "email",
        "label": "Email"
    },
    {
        "name": "name",
        "type": "text",
        "label": "Name [Deprecated]",
	"advanced": true,
	"help": "This field has been deprecated and divided into `First Name` and `Last Name` parameters."
    }
]

2. Adding a banner

If you need to make sure that the user notices the deprecated parameters, you can use the banner element.

There are three distinct message types available, each with a specific icon and guidelines for appropriate use. The recommended length of the message is 50 to 300 characters.

An example of an Information banner.
{
    "type": "banner",
    "title": "This is an info",
    "text": "Here is a description of info.",
    "theme": "info"
}
An example of a Warning banner.
{
	"type": "banner",
	"title": "This is a warning",
	"text": "Here is a description of warning.",
	"theme": "warning"
}
An example of a Danger banner.
{
	"type": "banner",
	"title": "Headline, if needed...",
	"text": "Minimum recommended length of message text. (50 characters)",
	"theme": "danger"
}

3. Do an additional check before the request is sent and throw an error if the deprecated parameter is present in the payload

If the called API service is too strict about using the deprecated parameters, you can do the error execution on the app's side.

Example of error execution
[
	{ // when parameter name exists
	"condition": "{{parameters.name}}", 
        "response": {
            "valid": {
                "condition": false, //to throw response as error
		"type": "DataError",
                "message": "You cannot use the parameter 'name' as it has been removed.\nPlease read the module's note message."
            }
        }  
    },
    { // when parameter name doesn't exist
	"condition": "{{!parameters.name}}",
        "url": "/api/users",
        "method": "POST",
	"body": "{{omit(parameters, 'name')}}",
        "response": {
			"output": "{{body}}"
		}
	}
       
]

How to shut down a module

Ideally, the removal of the module should be announced to the users in advance, so that they have enough time to update their scenarios.

Please, contact us via the helpdesk with a request for e-mail notification for users, that use the modules in question.

If you need to make sure that the module is not used anymore and the user acknowledges it at least from the error log, you can throw an error whenever the module is executed.

Example of module's sunset
{
        "response": {
            "valid": {
                "condition": "{{'a' === 'b'}}",
                "message": "You cannot use this module anymore as it has been shut down.\nPlease read the module's note message."
            }
        }
  }

How to deprecate a connection

Ideally, the removal of the connection should be announced to the users in advance, so that they have enough time to update their scenarios.

Please, contact us via the helpdesk with a request for e-mail notification for users, that use the connection in question.

If you need to deprecate a connection create a new connection, that should be used as the functional alternative, and rename the now deprecated connection so it now contains the (deprecated) string. Then, do the following:

  1. Remove the current primary connection.

  2. Map the new connection as the primary connection.

  3. Map the deprecated connection as the alternative connection.

Removal of primary connection
Mapping of a new primary connection
Mapping of a deprecated connection as the alternative connection
Logo
Type Coercions
Logo
Logo
Logo
Logo