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

Custom Apps Documentation

Get started

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Create your first app

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Debug your app

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

App Review

Loading...

Loading...

Loading...

Loading...

Loading...

Best practices

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 Maintenance

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

App components

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Overview

Make Custom Apps documentation is a guide for developers to create apps for themselves or others to use on the Make platform.

If you need to interact with a third-party application in Make and there is not an app created for it yet, you can make your own.

This documentation helps you prepare to build an app, walks you through the process of creating your first app, and provides you with best practices and specifications for your project.

Custom Apps Development Training

If you are new to custom apps, enroll in the Make Academy course Getting started with custom apps.

By the end of the course, you will understand what custom apps are and the benefits of using them, use the API documentation to help you design and create your first app, and identify the components that form a custom app.

Get started

Step 1: Select your editor

you want to use to build your custom app:

  • or

  • with vibe coding (information coming soon)

Step 2: Determine your starting point

  • If you're already familiar with Make, you're ready to .

  • If you're new to Make, we recommend that you begin with a practice exercise (see ).

Step 3: Practice connecting an API in Make

Before creating your first app, to practice connecting to your selected API on the Make platform. This will help you get comfortable with the process.

Select your editor

You can build a custom app in Make in a variety of ways.

Here we show two methods: through the Make web interface or with vibe coding.

Make web interface

The lets you create and manage the custom app configuration directly within the Make platform. It offers a graphical interface, allowing you to easily build the app by setting up connections, modules, and parameters and personalizing the app behavior.

Select the editor
Make web interface
create your first app
Step 3
make a test HTTP call
Vibe coding

Coming soon.

Make web interface

Modules

In this section, you will learn the naming conventions used for modules in an app.

  • Module names

  • Module labels

  • Module descriptions

Input parameters

In this section, you will learn the naming conventions used for input parameters of an app.

  • Parameter labels

  • Parameter hints

Overview

In our step-by-step examples, we use the Geocodify API. You can follow along with our example or you can select a different API to build your first custom app.

In this section, we will walk through the steps to create a custom app.

The information found here corresponds with the content of the Get started with custom apps online course and serves as an additional resource.

To create your first app, you will:

  • Complete the initial setup in Make

Overview

In this section, you will learn the best practices for developing custom apps.

  • Naming conventions

  • Input parameters

  • Output parameters

Modules names

Module names are unique identifiers for each module in your app.

They are internal names and differ from the module labels that a user sees when using the app.

Module names:

  • must be between 3 and 48 characters long

  • must match pattern /^[a-zA-Z][0-9a-zA-Z]+[0-9a-zA-Z]$/

A module name should not match any reserved word in JavaScript. See the list of reserved words in JavaScript .

Static parameters

A static parameter is a value or setting that is fixed, predefined, or constant.

Static parameters are only used for trigger modules.

For all other modules, the Static Parameters tab is no longer applicable and should be left empty. All fields should be added in the Mappable Parameters tab instead.

Base

The Base section should contain data that is common to all (or most) requests. At the minimum, it 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"

Connection types

If there are multiple types of connection, use parentheses for the connection type specification:

In this example, the additional type of connection is correctly specified with information in parentheses.

In this example, the connection types are incorrectly specified.

Bulk actions

Bulk action modules can perform an action on multiple records in a single call.

To use bulk action modules, the user needs to create an array of records and map the array into the bulk module.

  • In the bulk module UI, the mapping should always be turned ON by default.

  • Types of output:

    • If the response returns a single success / fail, the module should be an action module.

    • If the response returns an output bundle of success / fail for an individual record, the module should be a search module.

  • If possible, bulk modules should output the updated range/rows.

For more information for labeling and describing bulk action modules, see the best practices guide to and .

Make app environment

On the Custom Apps page, click on your app to view the Make app environment for your custom app.

Across the top you will see eight tabs.

In this overview, we will focus on the app's main components:

Test your app

To test your app in Make, create a new scenario.

1

In the Scenario Builder, add the Geocodify > Search geolocation module that you have just created.

Note that is has the Private tag.

2

Click Create a connection.

App logo

Every app in 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.

Overview

Learn how to use the Make DevTool to debug your app.

During the development of your app, you may experience issues with your app and/or API. Therefore, you will need to use the to debug your app.

Here are some examples of specific debugging issues you may encounter:

Make DevTool

With you can debug your Make scenarios by adding an extra pane to the . Using this new debugger pane, you can check all the manual runs of your scenarios, review all the performed operations, and see the details of every API call performed. Additionally, you can see which module, operation, or response caused an error in your scenario.

Install Make DevTool

To install Make DevTool:

1

Live Stream

Live Stream displays what is happening in the background once you've hit the Run once button.

You can 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, request headers, and query string)

  • Request Body

Scenario Debugger

The Scenario Debugger shows you the historical logs of your scenario, including the history of the scenario runs. You can search for modules by their name or ID.

Search for modules

To search for the module by name or ID number, enter the search term in the search field in the left panel of the DevTool.

Overview

When you create a custom app, you can use it any time. When you finish developing and testing your app, you might share the app with selected users with an invite link.

If you want to share your custom app with all Make users, we have created an app review process to ensure that your app meets our app standards.

Prerequisites

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

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 , Make will publish your app and make the app available to all Make users. Otherwise, the Make QA team will contact you with instructions for how you should improve your custom app.

Review status

You can check the status of your custom app review in the App flow tab. The App flow tab contains a list of apps under review, their status, and their description. When you click the App flow tab, your browser automatically scrolls and highlights the current review status of your app.

If you have published your app or requested a review for a new app or an app update, you will see 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.

Every entry contains 4 columns:

  • Date and Time - When the activity happened.

Approved app

When Make approves your app, your app becomes available to all Make users in their scenarios. To the users, an app developed by Make looks the same as the app developed by you. You need 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 is published, 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:

App
Description

Apps

Follow these conventions when naming your app:

  • Start with your brand guidelines as the primary reference.

  • When uncertain about naming conventions, use .

  • If your app name requires suffixes, use title case for the suffix as well.

Correct

Groups

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

Consider the following when you decide the order of groups and modules in your app:

  • Triggers

    • All instant triggers

    • All polling triggers

Output parameters

In this section, you will learn best practices regarding the interface and how to process output parameters.

Base URL

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

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 requested in the connection and then the value should be used in the Base tab.

Authorization and sanitization

The Base section should also have and that is common for all modules. The authorization should use the API key, access token, or username and password entered in the connection. The sanitization should hide all these sensitive parameters.

Editable connection

We recommend allowing users to after they create them. Updating a connection simplifies scenario and user credential maintenance when there's a change in the user's organization.

To allow users to edit a connection:

1

Go to the Parameters tab of the connection.

2

For each parameter with a value that should be kept secret, make sure that it is marked as type password.

Modules

In this section, you will learn best practices for the following regarding modules:

Approved apps

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.

You should always make sure the 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

Advanced inheritance

Consider this as the base:

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

This 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:

Private/Public apps

Changes

In custom apps that haven't been approved by Make, it's 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 the VS Code 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.

Connections

Every connection should have a way to check if the used API key (token) is valid (a validation endpoint).

This means that each connection should have a part that uses the API key (token) against an endpoint that requires only the API key to run. For example, a user info endpoint, or any endpoint that is used to list data.

The validation endpoint is located:

  • OAuth1 and OAuth2 - in the info directive

  • API key, basic auth, digest auth, other - in the url

You must ensure that you do not have JavaScript syntax warnings or errors in 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 affects all scenarios even if they run app 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 will ensure that everything is consistent and that there are no breaking changes made in the current app.

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, review the Updating your apps article 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. You also need to fix issues reported by users.

Read more about terms of approved app maintenance.

App name with a custom app tag

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.

App name 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.

Set up the Create a connection component for users to connect to your app
Specify the common directives in the Base
Create a module
Test your app
Base
Connections
Polling triggers
Instant triggers (scheduled)
Modules
Remote Procedure Calls (RPCs)
here
Make DevTool or the console
Pagination in list and search modules
Remote Procedure Calls (RPCs)
Custom IML functions

Request app review

After you confirm that your app complies with the prerequisites, you can request an app review.

Check the review status

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

Approved app

When Make approves your app, the app becomes available to all Make users. After approval, the process of your custom app management changes.

Maintenance of approved app

It is important to keep your app up to date and provide support to your app's users.

app review prerequisites
best practices
Incorrect
Note

Google Ads Reports

Google Ads reports

'Report' is a suffix and should be capitalized.

Instagram for Business

Instagram For Business

'for' is a preposition and should not be capitalized in title case.

X (formerly Twitter)

X (Formerly Twitter)

'formerly Twitter' is additional information. In this case, the additional information should be place between ( ) and the adverb should be lowercase.

title case
Interface
Processing of output parameters
Search modules
Module types
Batch actions
Bulk actions

Modules

Connections
Base

Action - Name of the activity.

  • Status - Available only with the action "Review status updated". The status of the app's review.

  • Comment (manual) - A comment with additional information or context.

  • 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 also check your app review status in your email. The email has the subject: "App review: YourAppName". Make keeps all communication about the app review in this email thread.

    :
    "[{{statusCode}}] {{body.error.message}} (error code: {{body.error.code}})"
    }
    },
    "log": {
    "sanitize": [
    "request.headers.authorization"
    ]
    }
    }
    module labels
    module descriptions
    {
        "headers": {
            "authorization": "Bearer {{connection.accessToken}}"
        }
    }
    {
        "headers": "{{headerBuilderFunction()}}"
    }
    {
        "headers": {
            "{{...}}": "{{headerBuilderFunction()}}"
        }
    }

    Action

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

    Action modules are straightforward modules that make one or more requests and return a single bundle as result. Each execution is isolated, so they do not have a state like polling triggers and they can't be used to output multiple bundles like search modules. You should use an action module when the API endpoint returns a single item in the response. Some examples of common action modules include:

    • Create an object

    • Update a user

    • Delete an email

    • Get a record (by its ID)

    • Download/Upload a file

    . 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.

    We recommend you 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.

    changes will not break
    approval of the changes
    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.

    [
        {
            "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.

    edit their connections
    authorization
    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>"
            ]
        }
    ...
    }
    3

    Enter your API Key from the Geocodify API.

    4

    Click Save.

    5

    Insert the address you want to geolocate, for example: Champ de Mars, 5 Avenue Anatole France, 75007 Paris, France.

    This is the Location parameter you set up in the Mappable parameters tab.

    6

    Click Save to save the module.

    7

    Save the scenario. A pop-up appears appears asking for confirmation to install the app in your organization.

    • Click Yes on the pop-up to install the app in your organization.

    • Click Run once to run the scenario.

    8

    Open the output to retrieve the coordinates. They are under Output> Bundle 1> response> features> 1> geometry> coordinates.

    The coordinates are output this way because you chose to return the response as is, without filtering or customization.

    Your app works. Congratulations!

    At this point, your module is hidden by default. Even if the app is installed in the organization, it won't appear in the scenario editor for other users until you make it visible.

    To let other users see your module, go to the module in the app setup and slide the toggle from hidden to visible.

    To learn more about app and module visibility, see the App visibility article.

    App logo

    To add a logo to your custom app, make sure your logo file 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 500 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 Lunapic free editor.

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

    Examples of how logos are rendered

    Example of logos with one color
    Example of grayscale logos
    Example of color logos

    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 also create a 3D effect.

    Example of apps with multiple transparency levels

    Update 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 needs to be approved by Make.

    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.

    Open the Chrome Web Store and search for Make DevTool to install the extension.
    2

    Click the Add to Chrome button. Confirm installation in the pop-up window.

    Make DevTool is now installed in the Chrome Developer Tools pane.

    Use Make DevTool

    To start using Make DevTool, open a scenario in Make, press Control+Shift+I (Windows) or Command+Option+I (Mac) on your keyboard to open Chrome Developer Tools, and switch to the Make tab.

    We recommend docking the Chrome Developer Console to the bottom to maintain a better view of your scenario.

    There are three main features accessible in the left side of the docked console - Live Stream, Scenario Debugger, and Tools.

    Make DevTool
    Chrome Developer Tools
    Response Headers
  • Response Body

  • After you run a scenario, select Live Stream from the left-side panel and click one of the tabs in the right panel of Make DevTool to view the desired information.

    Search in Logs

    Enter the search term in the search field in the left panel of Make DevTool to only display requests and responses that contain the search term.

    Clear the list of recorded requests

    To clear the list of requests recorded by Make DevTool, click the bin icon next to the search field.

    Enable console logging

    To enable logging in the console, click the computer icon next to the search field.

    When logging is enabled, the color of the computer icon switches to green.

    Click the same icon again if you want to disable logging. The icon turns gray when the feature is disabled.

    Retrieve 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 panel.

    Similarly, you can retrieve cURL using the Copy cURL button next to the Copy RAW button in the top-right corner of the Make DevTool's panel.

    Open module settings

    To open the module settings, double-click the module's name in the list.

    View request or response details

    To view request or response details, click the operation in the list.

  • Generic modules

    • Sorted in groups, if possible

    • Example of a group: RECORDS

  • Typical modules divided by business logic

    • Sorted in groups (examples: TASKS, DEALS, CONTACTS)

    • Sorted from the most important to the least important

    • Ordered by RCUD logic (read, create, update, delete)

  • Other

    • Examples: Make an API call, Execute a GraphQL query

  • Example of the order of groups
    Example of the order of modules in a group
    • TRIGGERS

    • FORMS

    • TASKS

    • DEALS

    FORMS

    • List forms

    • Get a form

    • Create a form

    • Update a form

    When dividing modules into groups by business logic, if every group only has one module, do not apply custom grouping. Instead, use the default groups: ACTIONS and OTHER.

    Correct group structure
    Incorrect group structure

    Correct Base URL examples

    An example from the Mailerlite app:

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

    An example from the Freshsales app:

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

    Incorrect Base URL examples

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

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

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

    Using the Base URL

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

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

    Modules "url" should start with forward slash /

    Base URL
    in the Communication tab

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

    The API key is checked against the /whoami endpoint. If the API key is incorrect, this returns an error and the connection won't be created.

    Documentation guide

    The custom apps 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 components

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

    The Make app environment main app tabs correspond to the app components in the documentation.

    Component blocks

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

    For example, parameters (static or mappable) are available everywhere apart from Base and Functions. Meanwhile, common data exists only inside Base and Connection.

    Information detailing these blocks can be found in the .

    Block elements

    are like bricks for component blocks. A block doesn't exist without these elements. Multiple elements form a block.

    Pick a brick that suits your needs. You can use multiple, separated components or combine them together using the nested property.

    Useful resources

    A collection of resources to help you develop your custom app and learn more about using Make.

    Resource
    Description

    Our online course where you can learn all aspects of custom apps development in Make.

    Our community dedicated to Makers who develop custom apps.

    150+ useful videos all about Make.

    Our documentation to help you learn more about the Make platform and the Scenario Builder.

    Initial setup in Make

    In our step-by-step examples, we use the Geocodify API. You can follow along with our example or you can select a different API to build your first custom app.

    To set up your custom app for Geocodify:

    1

    Log in to Make and go to the Custom Apps section.

    2

    In the upper-right corner, click + Create app.

    3

    In the pop-up window, fill in the app details. The chart below contains the values to use for your Geocodify app.

    Field
    Value
    Description
    4

    Click Save.

    You can now see your custom app listed on the Custom Apps page where you can view the to continue with the setup of the , , and for your app.

    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.

    In the dialog box, confirm the installation.

    A private tag is displayed after your app's name on the app's page.

    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

    You can delete a module for a private app only if it is not used in any scenario. If you try to delete a module used in a scenario, you will see a warning dialog with a request to remove the module from the scenario first.

    Deletion of a webhook, connection, RPC, or custom IML function

    You can delete a webhook, connection, RPC, or custom IML function only if it is not used in any module. If you try to do so, you will see a warning dialog in the lower-right corner.

    Public app

    If you want to share your app outside of your organization, you need to publish your app to generate an invite link.

    Go to your app and open your app's page. Click Publish in the right upper corner.

    Once you publish the app, the invite link is generated. You can share it with any other Maker who will then install it in their organization. It doesn't matter where the organization is located (EU1 or US1).

    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, and we will remove the app from your account.

    Deletion of a module, webhook, connection, RPC, or 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 to indicate that the component should not be used, e.g. [DO NOT USE] My unwanted module. Make sure the module is hidden.

    Distribution control of modules

    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.

    If you hide an already visible module that is currently in use in a scenario, the module will continue working in the scenario. However, the hidden module will not be available in the app selector in a new scenario.

    Approved app

    Approved apps are available to any user in Make. If you want to have your app available to all Makers, review the to ensure that your app follows best practices.

    Once you make sure your app follows our conditions and best practices, you can .

    If the app , it becomes available for everyone to use. That mean's that any user on Make can add your app to his/her scenario and use it.

    Pagination in list/search modules

    Debug pagination issues in list and search modules by creating test records and results

    If the API supports pagination, it should be implemented. To test that the pagination works as intended, we recommended you set the page size to a low number, if possible.

    {
        "url": "/contacts/filters/{{parameters.filter_id}}",
        "method": "GET",
        "qs": {
            //"per_page": 100
    	"per_page": 10 //set value for testing
        },
        
    

    Create test records

    Next, create as many records to equal one page and a few more.

    Use the Flow Control > Repeater module to do this, which also returns the ID of the repeat.

    Map this ID in the following module to differentiate the records.

    Once the test records are created, you can test your search module.

    Test search module

    With the help of the ID from the repeater, you can see if the records are ordered and how, and whether the records are correctly retrieved (for example, they are not being duplicated).

    In the console, you can also 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 see them.

    Custom IML functions

    Debug custom IML functions with a variety of tools

    To debug your IML functions, output the code and use a tool of your choosing.

    Output a message to the dev 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 are 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.

    During the run of a module, IML functions called inside this module will also run.

    Debug the JavaScript snippet

    To debug the output, you can use a variety of tools. Search online to find a JavaScript debugging tool that you prefer.

    Here are some to choose from:

    You can use this code to test the functionality of the JavaScript debugger to see how it works.

    Naming conventions

    In this section, you will learn the naming conventions used for elements of an app.

    • Apps

    • Modules

      • Module names

    Parameter labels

    Parameter (input field) labels should be clear and easy for users to understand. Consider the following when creating input field labels:

    • Give a short description (1–3 words) of the requested input.

    • Match the terminology that users see in the third-party’s UI, not the API documentation.

    • Be descriptive, not instructional. If the selection needs more explanation, include information in the help text below the field.

    • Use . The following should be capitalized:

      • The first word of the label

      • Proper nouns and trademarks. For example, names of people, companies, products, or other proper nouns

      • Official or trademarked terms

      • Acronyms. For example, Content ID, File URL

    • Avoid punctuation and articles (the, an, a).

    Parameter label examples

    Correct
    Incorrect
    Note

    Parameter label for fields with a Map toggle

    For select fields, the label of the field should be dependent on whether the Map toggle is ON or OFF by default.

    If the Map toggle is ON by default, the field label should contain "ID".

    If the Map toggle is OFF by default, the field label should NOT contain "ID".

    Interface

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

    You can generate an interface using our Interface Generator.

    Dynamic interface according to user-defined fields

    Retrieving all fields from the endpoint could be difficult, especially when integrating an ERP or other business-related backend that has hundreds of fields.

    The solution to this varies across different platforms, but they are similar.

    One method is to use a select parameter to retrieve only the fields that you need. For example, .

    With Name and IcaoCode as the only output, adjust the interface to indicate to users that only these fields are available for mapping.

    Connection metadata

    We recommend you use the metadata parameter to store the account's name or email. This allows users to easily distinguish their stored connections.

    Always save the metadata in the connection if:

    • the endpoint that can obtain the authenticated user’s information is available, and

    • the information provided is able to distinguish the connection.

    Saving the metadata allows for better identification on the Connections page.

    We suggest saving the following information:

    • Name

    • Email

    • User ID

    • Organization, Company, Location, etc.

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

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

    Additional OAuth scopes

    The Make an API Call module connection will not work if the required scopes of the endpoint are not in the OAuth connection. To correct this, allow users to define additional scopes when they create a connection.

    Make an API Call parameters: non-editable connection

    {
    	"type": "banner",
    	"text": "Your connection must contain the required scopes for your API call. If you receive an error, create a new connection with the necessary Additional scopes.",
    	"theme": "info"
    }

    If you encounter the warning String is longer than the maximum length of 256. , wrap it in an RPC.

    Make an API Call parameters: editable connection

    Connection parameters

    Connection communication

    Track code changes

    Make web interface

    After you save a change in an approved app, a new Changes tab is available. On this page, there is a list of all changes available in the particular app.

    After clicking on a Diff log, you will see a dialog box informing you about available changes.

    For a detailed Diff file, click Show diff button.

    You will see a comparison view with the changes that have been made.

    VS Code extension

    After you save a change in an approved app, the app is marked with a * next to the name. Every module with blocks where the changes are available is marked with a * as well.

    To view changes, right click the item and select the Show changes option.

    You will see a comparison view with the changes that have been made.

    Module types

    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 documentation as well as in the best practices guide to module descriptions.

    The List campaigns module in this example is incorrectly identified as an Action module.

    The List campaigns module in this example is correctly identified as a Search module.

    Universal modules

    Each app should have a universal module. This module allows users to use API endpoints that are not supported, using their connection created to the app.

    More about universal modules can be found in our documentation.

    Make sure the universal module has the:

    • correct label and description,

    • correct url which starts with the , and

    • correct connection.

    429 error handling

    An error with the status code 429 is an API rate limit error.

    However, the default module error type for an error code between 400 - 500 is always a RuntimeError.

    There are advantages to handling a 429 error as a RateLimitError instead.

    RuntimeError handling vs RateLimitError handling

    In a scenario with scheduling turned on, if one of the scenario modules throws a RuntimeError, your scenario will break and retry to run according to the number of consecutive errors from your scenario settings (the default is 3 times).

    If the number of consecutive errors is consumed, the scenario scheduling will be switched off and you will need to manually switch your scenario scheduling on again.

    To prevent this, use the module error type RateLimitError to handle the 429 error. This error type has the same functionality as ConnectionError and returns the warning message instead of the error sign.

    The advantage of using RateLimitError is that, instead of using the number of consecutive errors and then switching the scheduling off, the retries continue with .

    For example:

    • A scenario with scheduling turned on suddenly has one module throw a RateLimitError.

    • It will retry after 1 minute.

    • If it throws the RateLimitError again, it will retry after 2 minutes.

    Terms of approved app maintenance

    After 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

    • submitting API checkups

    • checking feature requests on the Make Idea Exchange regularly

    If you actively maintain the custom app, users can use the app with confidence. The users don't have to worry that their scenarios might 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 email 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

    Please ensure your integration is kept current with the latest API.

    If the Make integration is not actively maintained, we reserve the right to assume ownership of the custom app to ensure uninterrupted service for its users.

    Feature requests on Make Idea Exchange

    When Make users miss a feature in an app, they can submit a new request to the .

    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.

    Sanitization

    Sanitization protects sensitive data (passwords, secret keys, etc.) from leakage.

    You should always sanitize the log so no personal tokens and/or keys are leaked.

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

    Example of a log from the console with a sanitized access token
    ...
    "log": {
            "sanitize": ["request.headers.accesstoken"]
        }
    ...

    Accesstoken is correctly mapped, so it's not exposed.

    Without sanitization, there isn't a log in the list of executions in Live Stream.

    Additionally there will be no shown log in the Scenario Debugger, as shown in the screenshot above.

    Neither the developer nor user can see the original request and respond to debug possible issues.

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

    Base URL

    Base URL is the main URL to a web service that 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. For example, if the web service uses multiple domains you may want to let your users have access to the one they use.

    Base URL example: two types of accounts

    This is an example of how to handle two types of accounts - sandbox and production.

    1

    Add a checkbox in your connection parameters that can be checked when the condition is met:

    2

    Implement a condition in both the connection and the base:

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

    Base URL example: two environments

    This is an example of how to handle two types of accounts - eu and us.

    1

    Set up select in your connection parameters, where you let your users choose from available environments:

    2

    Map the environment in both the connection and the base.

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

    See the for more information.

    Dedicated

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

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

    Types of dedicated webhooks

    Attached

    For an , the new URL address that is created is automatically registered to the service using an attach procedure and can be unregistered using detach procedure.

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

    Not attached

    For a , the new URL address that is created has to be registered manually by the user. The 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 the attach directive.

    Base

    The Base component contains the common settings of the app that are inherited by all modules.

    The common settings include:

    • Base URL: The API base URL used as the main address for all requests to the API.

    • Authorization: Authentication information and credentials.

    • Error Handling and Sanitization: Same as the Connections component.

    Request app review

    Once you make sure that your app meets Make's and follows Make's , you can request an app review:

    Request a review

    To request an app review:

    1

    Remove the test modules or test connections before you publish an app. When your app is public, you can't delete its modules, connections, or other components.

    Update your app

    When maintaining your app, you might need to apply changes or create a new app version.

    The process is different between , and .

    Changes and versioning in private/public custom apps

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

    Connections

    APIs usually use authentication or authorization to limit access to their endpoints.

    Make provides a list of the most common types of connections with prefilled code that can be edited for your needs. In general, you will only need to change the URLs and names of the parameters.

    Types of connections

    Approval of changes

    All changes to approved apps require Make approval. Changes will not be available in the public version of the app prior to our approval.

    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 request a review of your changes and wait for approval.

    To request a review, 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 the 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 email confirming that we have logged your request. You can find the email by subject's name: "App Update Review: YourApp'sName".

    Attached

    The new URL address for the webhook, created when a user opens the instant trigger and creates a new webhook, is automatically registered to the service using the attach procedure, and can be unregistered using the detach procedure.

    Attach

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

    Shared

    Shared webhooks are not very common. They are used when the external service requires you to register a single URL for all events, for all users.

    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 a , not a shared one.

    In order to activate the shared webhook so it listens to incoming traffic, you must publish your app. Before doing so, please read the to understand how that affects your app.

    Not attached

    The new URL address for the webhook, created when a user opens the instant trigger and creates a new webhook, has to be registered manually by the user. The user needs to copy the URL address and paste it to the webhook configuration in the settings of the external platform. The main advantages of webhooks that are not attached when compared to the custom webhook module are:

    • Improved UX

      • Mapping is easier when you define an .

      • Ability to modify the output if needed, such as parsing dates in non-standard forms, or un-nesting parameters.

    Input parameters

    In this section, you will learn best practices regarding different types of input parameters and how to process them.

    Our senior developers will check your changes. The changes will be either approved or rejected.

    If approved, you will be informed by email that your changes have been approved.

    If rejected, our senior developer will contact you with further details and/or recommendations for the correct solutions.

    Changes that require approval by Make

    The following are changes that need to be approved by Make:

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

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

    • a new theme and/or logo of the app

    Rollback of changes in Make

    If you need to roll back any changes you have made so far, contact us.

    Module labels
    Module descriptions
    Input parameters
    Parameter labels
    Parameter hints
    Groups
    inform users about shutdowns and other cases in advance
    App Improvement Ideas board
    attached dedicated webhook
    dedicated webhook that is not attached

    100+ questions regarding custom apps development in 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.

    Regular expressions 101

    A recommended tool for experimenting with regular expressions. Make sure to tick the ECMAScript (JavaScript) FLAVOR in the left panel.

    Regular expressions generator

    A regular expressions generator for those who need help with regex.

    Lunapic

    A free editor to create and edit app logo files.

    Make support

    Contact us if you need help or you didn't find the information you need.

    Custom apps development training
    Make community - custom apps
    Make YouTube channel
    Make Help Center
    Stack Overflow
    Changes and versioning in approved custom apps

    Every change made to your approved custom 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 your updates to users.

    private/public apps
    approved apps
    private and public custom apps
    Mappable parameters
    Static parameters
    Processing of input parameters
    contact our support team
    app review procedure
    request an app review
    passes our review process
  • 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 that 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 so there are too many pages being retrieved (= too many calls).

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

  • "response"
    : {
    "output": "{{item.contact}}",
    "iterate": "{{body.data.contacts}}",
    "limit": "{{parameters.limit}}"
    },
    "pagination": {
    "qs": {
    "page": "{{pagination.page}}"
    },
    "condition": "{{body.data.max_page > body.data.page}}"
    }
    }
    Example of array output
    Example of paginated response

    Content ID

    Content id

    ID is an acronym and remains capitalized.

    Product API key

    Product API Key

    API is an acronym and remains capitalized. Key is lowercase.

    Website URL

    Website url

    URL is an acronym and remains capitalized.

    ID finder

    ID Finder

    ID is an acronym and the first word in the label.

    Custom data parameters

    The Custom Data Parameters

    First word capitalized, the rest in lowercase.

    Do not use the article ‘the’.

    Submit form

    Submit Form

    First word capitalized, the rest in lowercase.

    New Instagram account name

    New instagram account name

    Instagram is a proper noun, remains capitalized.

    Google Drive folder

    Google drive folder

    Google Drive is a proper noun, remains capitalized.

    sentence case capitalization
    OTHER

    Delete a form

    COMPONENT BLOCKS section
    Block elements
    When viewing module details, the tabs correspond to component blocks in the documentation.
    modules
    base URL
    Open Data Protocol (OData)
    GET serviceRoot/Airports?$select=Name,IcaoCode
    
    {
        "@odata.context": "serviceRoot/$metadata#Airports(Name,IcaoCode)",
        "value": [
            {
                "@odata.id": "serviceRoot/Airports('KSFO')",
                "@odata.editLink": "serviceRoot/Airports('KSFO')",
                "Name": "San Francisco International Airport",
                "IcaoCode": "KSFO"
            },
            ......
        ]
    }
    
    ...
    "response": {
        "metadata": {
                "type": "email", //allowed values are "email" and "text"
                "value": "{{body.data.user.email}}"
            }
    },
    ...
    Repeatedly the scenario increases the interval by 1, 2, 5, 10 minutes, and 1, 3, 12, and 24 hours.
    increasing time intervals
    {
      "response": {
        "error": {
          "429": {
            "type": "RateLimitError",
            "message": "{{body.message}}"
          },
          "message": "{{body.message}}"
        }
      }
    } 
    If the API returns a parameter that should 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).

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

    The webhook collection with the 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 need to do is to correctly specify the URL to detach a webhook. No response processing is needed.

    {
        "url": "https://www.example.com/api/webhook",
        "method": "POST",
        "body": {
            "url": "{{webhook.url}}"
        },
        "response": {
            "data": {
                "externalHookId": "{{body.id}}",
                "token": "{{body.token}}"
            }
        }
    }
    All debug messages that you specified in the IML function are available inside the developer console of your browser.
    JS Playground
    Mozilla developer playground
    RunJS
    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;
    }
    Output from the Scenario Debugger
    Example of a log from the console with an exposed access token
    best practices for Base URL
    {
        "url": "https://www.example.com/api/webhook/{{webhook.externalHookId}}",
        "method": "DELETE"
    }
    function myFunction(x) {
        x = x + 1;
        //do something
        console.log(x); // outputs the value of x at this point
        return x;
    }
    
    myFunction(1);
    {
    	"type": "banner",
    	"text": "Your connection must contain the required scopes for your API call. If you receive an error, edit your connection with the necessary Additional scopes.",
    	"theme": "info"
    }
    [
        {
            "name": "additionalScopes",
            "label": "Additional Scopes",
            "type": "array",
            "spec": {
                "type": "text",
                "label": "Scope"
            },
            "help": "Additional scopes are required for the __Make an API Call__ module. For details, see the [App Name API Documentation](https://link-to-doc). Add scopes for every API call you will make with this connection.",
    		"labels": {
    			"add": "Add scope"
    		}
        },
        {
            "name": "clientId",
            "type": "text",
            "label": "Client ID",
            "advanced": true
        },
        {
            "name": "clientSecret",
            "type": "password",
            "label": "Client Secret",
            "advanced": true
        }
    ]
    {
        "authorize": {
            "qs": {
                "scope": "{{join(distinct(merge(oauth.scope, ifempty(parameters.additionalScopes, emptyarray))), ',')}}",
                ...
            },
            "url": "...",
            "response": {
                "temp": {
                    "code": "{{query.code}}"
                }
            }
        }
    }
    ...
    "log": {
            "sanitize": ["request.headers.token"]
        }
    ...
    [
       {
          "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",
        ...
    }

    Description

    Provides geocoding and access to a spatial database.

    Optional: Description of the custom app.

    Theme

    #46367f

    Color of the app in the scenario editor. Specified with a hex code.

    Language

    English

    The language of your app.

    Audience

    Global

    Where the app is available. At the moment, this parameter doesn't have any effect.

    App logo

    Optional: Logo of the app used in the scenario editor. For more information on logo specs, see the .

    Name

    geocodify-app

    A unique identifier for your custom app. This is an internal name and is not visible in the scenario builder. Must match the following Regex: /^[a-z][0-9a-z-]+[0-9a-z]$/

    Label

    Geocodify

    Make app environment
    connection
    Base
    module

    Name of the custom app in the scenario editor.

    If a module doesn’t define a setting, it follows what’s in the Base. However, if a module specifies a different value for the same setting, for example a different error handling directive, this overrides what's present in the Base.

    The Base component is the code where you specify the common directives and the common data if needed. When you create a new app, the Base is filled in with default code.

    Set up Base for your Geocodify custom app

    To set up the Base for your custom app:

    1

    Remove the default code that is present.

    2

    Copy and paste the following code:

    Code
    Description

    "baseUrl": "https://api.geocodify.com/v2",

    Contains the base URL of the API. Note that it contains the API version.

    3

    Click Save changes.

    Continue to set up the module.

    2

    Navigate to your custom app's code and click the Publish button.

    Once you publish your app, you can't unpublish it. See the app visibility section for more details.

    3

    In the Modules tab, click the switch to set the visible status of each module in the app that you want to publish.

    4

    You will see a new Review tab in the top panel.

    5

    Complete the form on the Review page.

    • 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 submit is 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 changes to pending approval.

    Once you request the app review, Make sends you an email with the subject: "App review: YourApp'sName". If you have any questions or additional information to share, reply to the email.

    Each time you update your app based on reviewer feedback, 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 the Update review button

    Review process

    The review process contains three 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. This 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 contacted 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 the .

    • Logo of your company - Applicable for ISV. The logo should appear on the app's landing page on make.com.

    • URL to the service - The 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 for 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.

    The 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.

    requirements
    best practices

    JWT

  • OAuth 1.0

  • OAuth 2.0

  • Common data

    When you use an OAuth connection, an ID and secret are generated for your user. To store them, you should use the common data inside the connection.

    Once the app is approved, the common data is locked and can't be changed due to security reasons.

    Inside the connection, common data can be accessed by the common.variable IML expression.

    Common data is encrypted when stored in Make.

    Reserved words in connections

    Reserved words are variables used internally by Make. Using reserved words for the parameter name key can lead to unexpected results. Avoid using a reserved word.

    Make reserved words are:

    • accountName: name of the connection used by the app module,

    • teamID: ID of the team to which the active user is currently assigned.

    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 the name key set to preserved word accountName.

    The code 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.

    Basic connection
    Implementation

    Shared webhooks must be registered in the external platform by the developer of the app. All notifications from the service for all users will be sent to Make through this single URL, which is generated when creating the shared webhook. On Make's end, the corresponding user account will be matched by it's uid.

    You should always follow the API documentation for the platform for which you are developing the integration.

    Matching the user's account with an incoming event

    Since the webhook URL is shared among multiple users, there must be a way to match the incoming events with their owners and deliver them correctly. This is done through the uid parameter, which must be defined both in the connection and in the webhook communication.

    Since the uid from the connection is needed to allow Make to match the incoming data to its intended recipient, all shared webhooks must have a connection attached to the webhook.

    {
        ...
    
        "info": {
            "url": "https://example.com/api/me",
            "headers": {
                "authorization": "Bearer {{connection.accessToken}}"
            },
            "response"
    

    Notice the uid parameter in the response object.

    Notice the uid parameter in the webhook's communication.

    dedicated webhook
    app visibility article
  • Improved discoverability

    • Easier for most users to notice as a viable option for their integrations.

  • If your intend to have your app approved by Make, you must provide instructions on how to connect the webhook in the app's documentation.

    Most webhooks that can't be attached automatically should not have a connection linked to them. However, there are a few notable exceptions where you might still need it:

    • One or more RPCs are used in the parameters of the webhook.

    • One or more RPCs are used in the instant trigger module.

    • One or more additional requests are defined in the instant trigger communication (for example, a webhook that sends only a resource ID with a request to retrieve the actual data for that resource).

    interface

    Remote Procedure Calls

    Use the Make web interface to debug Remote Procedure Calls (RPCs)

    To find the RPC debug tool:

    1

    Navigate to the Custom apps area.

    2

    Select your custom app from the list.

    3

    Click the Remote Procedures tab.

    4

    Select the RPC you want to debug.

    5

    Click Test RPC.

    RPC page

    Compare the tabs below to learn how to access information about your RPCs in Make.

    Communication tab

    After creating a new RPC, you can modify the default template in the Communications tab for your needs.

    Inside RPC, you can use the relative path and the full form of the URL.

    However, we advise using the relative path across all your RPCs and modules.

    A relative path is added to the "baseUrl" that you need to specify inside the app Base.

    Parameters tab

    By default, the Parameters tab is empty. Here you can add any parameter you need. You can also add 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

    The RPC debug tool works the same way modules do.

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

    2. Click the Test button.

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

    You will see the output that 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.

    Debug RPCs in the dev console

    Sometimes you may need to look into the request you're sending and the response from the API.

    You can see more details about the RPC you're testing by opening your browser's dev console: Network tab.

    The debug values show the request being sent and the response received by the API.

    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

    Review the following sections to learn more about each prerequisite.

    Custom app 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 its 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.

    Custom app code

    When developing a custom app in Make, you should first go through our 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 of sensitive data, e. g. API key or token.

    • The base and connection have .

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

    • The connection is . If the user uses incorrect credentials, they get an error.

    The items above are mandatory for each app.

    Testing your custom app

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

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

    Best practices for test scenarios

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

    • Try to put all of the 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 perform. For example, Create a Task > Create a Subtask (in order to create a subtask, the task has to be created first).

    • 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, .

    • Create a separate scenario that produces an error message. Run the scenario to get an 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.

    Error handling

    It is important to implement error handling in your app so your users clearly understand the cause of the error.

    Each service sends an error message when an error occurs. This usually happens when the request has wrong parameters or values or when the service has an outage.

    When an error occurs, the module should indicate the error as well as a detailed message that can then be used in filters. The message should be clear and user-friendly. For example, instead of "Error: contact_not_found", your message should be "[404] The specified contact was not found."

    The error handling code should correspond to the structure of the server response.

    Error handling example

    In this example, the JSON response has the following format in cases where something goes wrong:

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

    The error object in this 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. In the case of HTTP error 400, for example, the error message could look like this:

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

    Common error messages

    4xx: Client Error

    indicate something wrong on the client side.

    5xx: Server Error

    indicate something wrong on the side of the third-party service.

    Handling status code 200

    Sometimes the API returns status code 200 with an error in the body. In this situation, use the directive, which tells whether the response should be processed or an error should be thrown.

    It is NOT possible to use specific HTTP code 200-399 error directives without the using directive.

    Sometimes the API returns status code 200 without errors to an incorrect request. This can happen, for example, if the Get a/an [item] module is developed based on the search endpoint and not based on a separate endpoint. This may be the case if a separate endpoint for that action is not implemented on the service’s side.

    Incorrect example of the search approach for a Get module:

    If an incorrect ID is provided, this will return an empty response. It is important to validate the call's response and, if empty, return a formatted error message.

    Correct example of Search approach with validation:

    In this case, we mark the response as invalid if it has no ID, as the ID should always be returned with other data.

    JWT

    There's no dedicated JWT connection type because the JWT is a special format of the Authorization header. See the basic connection section for more information.

    Generating a JWT

    To generate the token, use the following code:

    {
      "url": "https://mock.api/connect",
      "temp": {
        "jwt": {
          "iss": "https://iam.the.issu.er",
          "iat": "{{timestamp}}",
          
    

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

    In this example, you can build a JWT payload inside the temp variable called jwt.

    Inside the Authorization header, call the IML function named jwt. The jwt function accepts four parameters:

    1. The payload to be signed.

    2. The secret to sign the payload.

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

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

    Modules

    There are six basic types of modules:

    Type
    Description

    ​

    Use if the API endpoint returns a single response. For example, Create a book, Delete a book, or Get a book.

    ​

    Use if the API endpoint returns multiple items. For example, List books finds specific books according to search criteria.

    ​

    Use to watch for any changes in your app or service. For example, Watch a new book is triggered whenever a new book has been added to the library.

    ​

    Use if the API endpoint has a webhook available (dedicated or shared). For example, Watch a new event.

    Make web interface

    The Make web interface lets you create and manage the custom apps configuration directly within the Make platform.

    Access the Make web interface

    To access the web interface:

    1

    Log in to Make.

    Make a test HTTP call

    In our step-by-step examples, we use the Geocodify API. You can follow along with our example or you can select a different API to build your first custom app.

    Now that you have , we recommend completing these first steps before :

    Remote Procedure Calls

    are used to retrieve live data from a service for an input field.

    You can use RPCs to retrieve dynamic options in a field to clarify the expected input for a user. For example, selecting a country in a dropdown could trigger an RPC to retrieve corresponding states or cities.

    In this example, the Priority field provides the expected values (Low, Medium, High, Urgent). In mapping mode, the user has to enter or map the option's ID instead of selecting a value.

    Polling triggers

    A polling trigger checks for new data in a service account since the last scenario run, based on the scenario's schedule.

    The polling trigger sends a request to the service. If new data exists, the scenario runs and you see the data in the module’s output as bundles. If not, you see no bundles.

    Use a polling trigger only if the results:

    • consist of either a numeric ID or a date as the identifier, or

    • the results can be sorted or defaulted in descending order.

    Authorization

    The Base section is used to set up authorization. Most services require the authorization key be sent either in headers or in the qs (query string). If you set the authorization in the base, all modules and Remote Procedure Calls (RPCs) will inherit it.

    Common methods of authorization

    {
    	// Default request configuration
    	"baseUrl": "https://api.geocodify.com/v2", // Default base URL for all modules and RPCs.
    	"qs": { // Default query parameters for all modules.
    		"api_key": "{{connection.apiKey}}" // API key, which user will provide in the connection as parameter.
    	},
    
    	// Default response handling
    	"response": {
    		"error": { // Error handling
    			"message": "[{{body.meta.code}}] {{body.meta.error_detail}}" // On error, returns error detail
    		}
    	},
    
    	"log": {
    		"sanitize": [ // Excludes sensitive parameters from logs.
    			"request.qs.api_key" // remove api_key query param from logs.
    		]
    	}
    }
    [
        {
            "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
        }
    ]
    {
    	"uid": "{{item.uid}}",
    	"output": "{{item.data}}",
    	...
    }
    :
    {
    "uid": "{{body.user.id}}",
    "valid": "{{body.ok}}",
    "metadata": {
    "type": "text",
    "value": "{{body.user.fullName}} ({{body.user.email}})"
    }
    },
    "log": {
    "sanitize": [
    "request.headers.authorization"
    ]
    }
    }
    ...
    }
    . This parameter is optional.
  • A custom header to customize the JWT authorization header. This parameter is optional.

  • "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)}}"
    }
    }
    npm library
    App logo article

    Use to enable users to perform an arbitrary API call to the service. For example, Make an API call and Execute a GraphQL query.

    ​Responder​

    Use to send processed data back to a webhook.

    Action​
    Search​
    Trigger (polling)​
    ​Instant trigger (webhook)
    ​Universal​
    Learn more about using RPC parameters

    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.

  • If your custom app is a scraper app, it can't include the name of a third-party service in your app or module names.

  • 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 a limit parameter.

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

  • Best Practices guide
    sanitization
    error handling
    base
    using a correct URL
    follow the steps here
    Example of a test scenario
    Example of an error

    "api_key": "{{connection.apiKey}}"

    Contains the query parameter for authentication. Note that the apiKey parameter is taken from the Connections component and accessed using connecotin.apiKey.

    "response": { "error": { // Error handling "message": "[{{body.meta.code}}] {{body.meta.error_detail}}" } },

    Instructions on how to handle errors. Note that in this case the code is the same as in the Connections component.

    "log": { "sanitize": [ "request.qs.api_key" ] }

    Instructions on the information that is saved in the logs.

    sanitize specifies what needs to be omitted.

    Note that in this case the code is the same as in the Connections component.

    make.com integrations page

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

    Note that this approach will trigger error handler from the Base, so make sure you have meaningful error messages there.

    4xx errors
    5xx errors
    valid
    valid
    {
        "error": {
            "code": "E101",
            "message": "The company with the given ID does not exist."
        }
    }
    "response": {
        "error": {
            "message": "[{{statusCode}}] {{body.error.message}} (error code: {{body.error.code}})"
        }
    }
    "response": {
        "error": {
            "message": "{{body.error.text}}"
        }
    }
    API key in headers

    API key in query string

    OAuth 2 access token in headers

    {
        "headers": {
            "x-api-key": "{{connection.apiKey}}"
        }
    }
    {
        "qs": {
            "apikey": "{{connection.apiKey}}"
        }
    }
    {
    	"url": "/items",
    	"method": "GET",
    	"qs": {
    		"id": "{{parameters.itemId}}"
    	},
    	"response": {
    		"output": "{{body.items[1]}}"
    	}
    }
    {
    	"url": "/items",
    	"method": "GET",
    	"qs": {
    		"id": "{{parameters.itemId}}"
    	},
    	"response": {
    		"valid": {
                "condition": "{{body.items[1].id}}",
                "message": "Item with the given id does not exist."
            },
    		"output": "{{body.items[1]}}"
    	}
    }
    {
        "headers": {
            "authorization": "Bearer {{connection.accessToken}}"
        }
    }
    2

    In the left navigation, click Custom Apps.

    You may need to first click More to see this option.

    The Custom Apps dashboard is where you can create a new app, see your existing apps, and see the status of your app (public or private).

    See the Create your first app section to get started.

    Web code editor key features

    After completing the initial set up of your app, you can use the web code editor to build your app.

    The following are helpful features of the editor:

    1. Hints with links to documentation for more information.

    2. A format code button with a label showing the data format. The available data formats are:

      • jsonc

      • json

      • javascript

      • markdown

    3. Context-aware code suggestions for the jsonc data format. The web code editor gives you hints about parameters or properties you can add to the custom app code. The code suggestions even work 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. A Save changes button to save changes in all editor boxes on the page.

    1. Hovering over built-in IML functions shows detailed information.

    1. Hovering over custom IML functions 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 keyboard shortcuts

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

    For Windows users:

    Shortcut
    Description

    Ctrl + Shift + H

    Show a cheat sheet with selected shortcuts

    Ctrl + S

    Save 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

    For MacOS users:

    Shortcut
    Description

    Ctrl + Shift + H

    Show a cheat sheet with selected shortcuts

    Cmd + S

    Save 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

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

    • MacOS

    • Windows

    Your browser might interpret some keyboard shortcuts differently. For example, the shortcut Ctrl + N creates a new file in VSCode, but in Google Chrome the shortcut creates a new browser window instead.

    Review the Geocodify API documentation
  • Do a Postman test

  • Make an HTTP call

  • Get an API token

    To get your API token from Geocodify:

    1

    Got to the Geocodify website and click Sign Up to create an account.

    2

    Enter your details and click Register (or log in Google or GitHub).

    3

    At the bottom of the Overview page in your dashboard, copy your API Key and store it in a safe place.

    You will use the Geocodify API key later, when you create your app.

    Review the API documentation

    To plan the HTTP call, study the Geocodify API documentation, paying particular attention to the information regarding authentication, the API Base URL, the API endpoints, and the query parameters for the Search API.

    API documentation section
    Description

    Authentication

    This section provides authentication details.

    For this API you need to use an API key, which should be included in your request query string using the api_key parameter, along with the value that you obtained from the Geocodify website.

    API Base URL

    This section provides the base URL that you will use to access all the API endpoints. The base URL is https://api.geocodify.com/v2

    API Endpoints

    This section lists all the available endpoints.

    For this use case, you will use the /geocode endpoint to get the coordinates of a specific address.

    Query parameters for the Search API

    api_key: The API key used for authentication.

    q: The address for which you want to retrieve the coordinates.

    The documentation doesn't specify the HTTP method, but since you are retrieving coordinates, you should use the GET method.

    Do a Postman test

    It is good practice to test the HTTP call using Postman before building it in Make. This ensures that everything is working properly and helps you plan the HTTP call setup in Make.

    1

    In Postman, create a new request and add the necessary elements to make the HTTP call:

    • Method: GET

    • URL (base + endpoint): https://api.geocodify.com/v2/geocode

    • Query parameters:

      • api_key: your API key

      • q: address to search, for example Champ de Mars, 5 Avenue Anatole France, 75007 Paris, France

    2

    Click Send.

    The API responds (HTTP 200 OK successful response status code) with a JSON file containing the address information and the API key.

    If something is not working properly, use the error that the API returns to troubleshoot any issues.

    Make an HTTP call

    After a successful Postman test, build your call using one of the HTTP modules in Make.

    According to the Geocodify API documentation, you need to provide an API key as a query parameter.

    While you could choose to make a generic request, the best option is HTTP > Make an API Key Auth request. This module lets you hide the API key, so it doesn’t appear in the scenario log and others can't see it when editing your scenario.

    1

    In the Scenario Builder, add the HTTP > Make an API Key Auth request module.

    2

    Click Create a keychain.

    Change the name of the connections if you wish, then fill in the following:

    • Key: your Geocodify API key

    • API Key placement: In the query string

    • API Key parameter name: api_key

    3

    Click Create.

    4

    Add the necessary information for the API call, including the address for which you want to retrieve the coordinates:

    • URL (base + endpoint): https://api.geocodify.com/v2/geocode

    • Method: GET

    5

    Click Save.

    6

    Save your scenario and click Run once.

    After a successful run, your module output can be found under Output> Data > Response > Features> 1> Geometry> Coordinates.

    You are now ready to create your custom app.

    selected the editor you want to use
    building your first app
    Get an API token from Geocodify
    In mapping mode, the user has to enter or map the option's ID instead of the label.

    RPCs are also helpful when a user only needs to understand the functionality of the module, primarily 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

    Mode edit for Get, Update, and Delete modules

    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 get the value from an RPC.

    Mode edit for Search and List modules

    For Search and List modules, the use of mode edit depends on the hierarchy level of the entity. If the entity is higher in the hierarchy, for example a "customer" or a "deal" and there are not many RPCs in the input, the mode should not be set to "edit".

    However, if the entity is low in the hierarchy, for example "email attachment", it should have the mode set to "edit" since the user will probably not want to list attachments of a single email repeatedly.

    Mode edit for Create modules

    Create modules should not have the mode set to "edit" unless it contains a very large number of RPCs in its input. This is because preloading a large number of RPCs would significantly increase the wait 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 how many objects the RPC shows to limit the load time.

    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 the 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.

    Example of a missing limit
    Remote procedure calls (RPCs)

    Using unordered or asc order in a polling trigger may not work due to a 3200 pagination limit, which could result in the trigger not returning new items.

    However, if the API accepts filtering and gets results only after a specified numeric ID or date, this could reduce the number of items.

    Epoch

    The Epoch tab of a polling trigger defines the look of the Choose where to start setting so a user can select a point in the past from where the trigger should start to process data.

    Use the limit parameter to restrict the number of results returned in the RPC, to avoid issues should the user have too many objects.

    ‌The limit parameter should be a static number that should be equal to, at maximum, 300 or 3 times the number of objects per page.

    The code is missing a limit parameter.

    The code includes a limit parameter.

    API endpoint requires from and/or to date parameters

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

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

    In this module example, the From and To parameters 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.

    In this module example, there aren't any required From or To fields.

    The mapped data.lastDate IML variable is available to the user in the Choose where to start setting, defined in the .

    The behavior of the supported options:

    Instant triggers (scheduled)

    An instant trigger is a webhook that starts a scenario whenever new data arrives.

    Your app should implement a fully functional instant trigger as a Watch module, even if an API might have endpoints like Get a webhook, List webhooks, or Update a webhook.

    Connect metadata

    Always save the metadata in the connection if

    • the endpoint that can obtain authenticated user’s information is available, and

    • the information provided is able to distinguish the connection.

    When naming your connection, provide as much information as possible. This provides better identification on the connection page. The following information is suggested:

    • Name

    • Email

    • User ID

    • Organization / Company / Location / Tenant

    Example - Constant Contact

    UID

    Always save the UID in the connection if the service supports a single webhook URL per app (a shared webhook). The saved UID must match the ID that is included in the incoming webhook payload.

    Example - Highlevel OAuth 2.0

    Search modules

    Search modules are modules that returns multiple results, as opposed to action modules that return only a single result.

    If you want to retrieve all users who are registered on your service, you can't use an action module because it only returns one result. Instead, use a search module.

    For example, if you call /users, you will get a list of users in body.data.

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

    Iteration and pagination

    Since search modules return multiple results, they should contain pagination and iterative directives.

    An action module should never contain pagination or the iterate directive. To return multiple objects, create a search module instead.

    Pagination parameters

    The pagination section should only contain parameters that relate to 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.

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

    Page size in pagination

    The page size should be as large as possible to reduce the number of requests, minimize delay, and avoid hitting the rate limit.

    Examples

    ActiveCampaign

    :

    Parameter
    Description

    In this case, set limit to 100 in the request.

    Productive

    :

    Parameter
    Description

    In this case, set page_size to 200.

    Limiting output

    Search modules should allow users to limit their output (how many bundles they return).

    This can be achieved by setting 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.

    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.

    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:

    • Base URL

    • Authorization

    • Error handling

    Key
    Type
    Description

    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 sanitization. Those parameters will be used across all the modules and RPCs.

    Common data

    Once the app is approved, the common data gets locked and it cannot be changed due to security reasons.

    Common data can be accessed by the common.variable IML expression.

    Common data is stored in encrypted form in Make.

    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.

    Modules

    In Make, you can set up , each with a specific function.

    In this tutorial, you will set up a Search module to retrieve the coordinates of an address.

    The Search module sends a request and returns multiple results. Use this module when you want to let users search for records.

    Inside the Search module component there are five tabs:

    Communication

    Information on what the engine needs to do to manage the API call (call the endpoint, process the response, pagination, etc). Remember that the following elements are

    Tools

    The Tools section of the Make DevTool has useful features that can help you build your scenario.

    Focus a module

    Opens settings of the selected module.

    The module ID is the number you see in the Scenario Builder next to the name of the module. The same apps used multiple times in one scenario will have different IDs for each module.

    Module labels

    Every module should have a label that precisely describes the module's use. 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.).

    Module labels follow . Only the first word is capitalized.

    There may be times when the third-party UI does not match these suggested naming conventions. In such cases, use the third-party terminology that is familiar to your users.

    Watch modules

    Watch modules watch for new data in a service and return it. They are trigger and instant trigger (webhook) modules.

    Module descriptions

    Module descriptions should emphasize what a user can achieve with the module, not necessarily the process it takes to perform the task. Write the description in the third person and capitalize only the first letter of the word in the description.

    There are 3 categories of information that the descriptions can include:

    1. End result (mandatory): Clearly describes what the module achieves.

    2. Restrictions/Requirements (when necessary): Include additional information if there is any other additional information that provides clarity for the user regarding what the end result will be, or specifics of the module.

    Module actions

    You can define a module's actions to take advantage of features.

    Create

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

    {
        "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}}"
    		}
    	}
    }
    "mode": "edit",
    Find module(s) by mapping

    Searches module(s)' values for the specified term.

    Field
    Description

    Keyword

    Enter the term you want to search for and click the Run button. The numbers in the output are IDs of modules tha contain the term you have entered.

    Use only values

    Enable this option to only search in module fields' values. Disable this option to also search in module fields' names.

    Get app metadata

    Retrieves app metadata by the app's module name or ID. This is useful when you need to get the app version used in your scenario for technical support or development of the app.

    Copy mapping

    Copies values from the source module to the target module. When the source and target modules are specified, click the Run button to copy the mapping.

    Field
    Description

    Source module

    Select the module or enter the module ID of the module from which you want to copy field values.

    Target module

    Select the module or enter the module ID of the module into which you want to insert the source module values. The values in the target module will be overwritten.

    Copy filter

    Copies the filter settings from the source module to the target module. The copy action is performed on the filter placed on the left side of the selected module. When the source and target modules are specified, click the Run button to copy the filter.

    Field
    Description

    Source module

    Select the module or enter the ID of the module from which you want to copy filter values.

    Target module

    Select the module or enter the ID of the module into which you want to insert the filter values from the source module. The values in the target module will be overwritten.

    Preserve fallback route setting

    If enabled and the source filter is set as the fallback route, then the target filter is also set at the fallback route.

    Swap connection

    Duplicates a connection from the source module to every module of the same app in the scenario.

    Field
    Description

    Source module

    Select the module or enter the ID of the module from which you want to duplicate the connection and set the same connection for every module of the same app in your scenario.

    Swap variable

    Searches for specified variables in the scenario and replaces them with a new variable.

    Field
    Description

    Variable to find

    Select the variable you want to replace from the module in your scenario and copy it to this field. You can also drag and drop the variable into the field.

    Replace with

    Copy and paste, or drag and drop the variable you want to use instead of the variable specified in the field above.

    Module

    Select the module in which you want to replace the variable. If no module is selected, the variable will be replaced in the entire scenario.

    Swap app

    Replaces the selected app version in your scenario with another app version.

    Before swapping, make sure that the version you've selected supports all the modules and functions you might need for your scenario.

    Base 64

    Encodes the entered data to Base64 or decodes Base64. This is useful when you you want to search for particular data in the encoded request. When the input is specified, click the Run button to perform the action.

    Field
    Description

    Operation

    Select whether you want to encode the data from the Raw Data field to Base64 or decode Base64 to raw data.

    Raw Data

    Enter the data you want to encode to Base64 or the Base64 you want to decode to raw data, depending on the option selected in the Operation field above.

    Copy module name

    Copies the name of the selected module to your clipboard. When the module is selected, click the Run button to copy the module's name.

    Field
    Description

    Module

    Select the module whose name you want to copy.

    Remap source

    Changes the mapping source from one module to another. You can do this for an existing module in your scenario as well as a new one. Click the Run button to perform the action.

    Field
    Description

    Source module

    Select the module to be replaced as the mapping source for other modules in your scenario.

    Target module

    Select the module you want to use as the new mapping source.

    Module to edit

    If you don't want to change the mapping in the entire scenario, select the module you want to change the mapping for.

    Highlight app

    Highlights modules of the specified app in your scenario.

    Field
    Description

    App to be highlighted

    Select the app you want to highlight in your scenario.

    Version

    Select the version of the app you want to highlight.

    Highlight color

    Enter the hex color you want to use to highlight modules in your scenario.

    Get blueprint size

    Checks the size of modules in the scenario. This is useful when you are having trouble saving a blueprint that is too large.

    Showcase mode

    Toggles the showcase mode of the scenario editor. This mode is useful when you are building a scenario and don't want to set up the full module.

    To leave showcase mode, run this tool again or save the scenario and refresh the browser window.

    Mock labels

    Changes the label and description of the given module. Changes made by this tool are not permanent and don't affect the real scenarios. This option is meant for presentation purposes only. To reset the text, refresh the browser window.

    Field
    Description

    Module

    Select the module to change the label and description.

    Label

    Enter the label of the module. An empty value equals no change. If you want a blank label, enter (space).

    Description

    Enter the description of the module. An empty value equals no change. If you want a blank label, enter (space).

    Change background

    Temporarily changes the background color of the scenario. This is useful when you need a white background for screenshots and mockups. To reset the background, refresh the browser window.

    Ctrl + /

    Toggle line comments

    F1

    Display web code editor command and shortcut reference

    F1

    Display web code editor command and shortcut reference

    Query string: q=Champ de Mars, 5 Avenue Anatole France, 75007 Paris, France
  • Parse response - Yes (this allows you to map the response items if needed)

  • 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 a GET request.

    {
        "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 GET method.

    List/Search modules return multiple bundles and should be a Search module type.

    Get modules return only one bundle (specified by the entered ID) and should be Action modules.

    Search module

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

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

    Update

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

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

    There are two types of update approaches - partial and full.

    Delete

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

    {
        "url": "/contacts",
        "method": "POST",
        "body": {
            "{{...}}": "{{omit(parameters, 'date')}}",
            "date": "{{formatDate(parameters.date, 'YYYY-MM-DD')}}"
        },
        "response": {
            "output": "{{body}}"
        }
    }
    {
        "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}}"
        }
    }

    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.

  • Epoch tab

    The pagination directive correctly contains only the "offset" parameter.

    }
    }

    limit

    The number of results to display in each page (default = 20; max = 100).

    offset

    The starting point for the result set of a page. This is a zero-based index. For example, if there are 39 total records and the limit is the default of 20, use offset=20 to get the second page of results.

    current_page

    1 by default or the value you put in page[number]

    total_pages

    total_count/page_size rounded up

    total_count

    Total number of resources you have

    page_size

    30 by default or the value you put in page[size]

    max_page_size

    200

    ActiveCampaign API pagination documentation
    Productive API pagination documentation

    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.0 Parameter Specification

    Collection of directives containing parameters for the OAuth 1 protocol.

    baseUrl

    String

    Base URL is the main URL to a web service. It should be used for every module and remote procedure in an app.

    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.

    Sanitization
    {
        "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"
            ]
        }
    }
    
    
    "response": {
                "output":
                {
                    "myArray": "{{body}}"
                }
            }
    {
        "url": "/contacts/{{parameters.contact_id}}",
        "method": "PUT",
        "body": "{{omit(parameters,'contact_id')}}",
        "response": {
            "output": "{{body}}"
        }
    }
    
    {
        "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": "DELETE",
        "response": {
            "output": "{{undefined}}"
        }
    }
    ...
    "qs": {
             "from": "{{data.lastDate}}",
             "to": "{{now}}"
          },
    ...
    
    {
    	"info": {
    		"url": "https://api.cc.email/v3/account/summary",
    		"method": "GET",
    		"headers": {
    			"authorization": "Bearer {{connection.accessToken}}"
    		},
    		"response": {
    			"error": {
    				"message": "[{{statusCode}}]\n{{parseError(body)}}"
    			},
    			"uid": "{{body.encoded_account_id}}",
    			"metadata": {
    				"type": "text",
    				"value": "{{body.contact_email}}"
    			}
    		},
    		"log": {
    			"sanitize": ["request.headers.authorization"]
    		}
    	}
    }
    {
    	"url": "https://services.leadconnectorhq.com/locations/{{connection.locationId}}",
    	"method": "GET",
    	"headers": {
    		"Authorization": "Bearer {{connection.accessToken}}",
    		"Version": "2021-07-28"
    	},
    	"response": {
    		"uid": "{{connection.locationId}}",
    		"metadata": {
    			"type": "text",
    			"value": "Location: {{body.location.name}}"
    		}
    	},
    	"log": {
    		"sanitize": [ "request.headers.authorization" ]
    	}
    }
    {
        "type": "ContactCreate",
        "locationId": "ve9EPM428h8vShlRW1KT",
        "id": "nmFmQEsNgz6AVpgLVUJ0",
        "address1": "3535 1st St N",
        "city": "ruDolomitebika",
        "state": "AL",
        "companyName": "Loram ipsum",
        "country": "DE",
        "source": "xyz form",
        "dateAdded": "2021-11-26T12:41:02.193Z",
        "dateOfBirth": "2000-01-05T00:00:00.000Z",
        "dnd": true,
        "email": "[email protected]",
        "name": "John Deo",
        "firstName": "John",
        "lastName": "Deo",
        "phone": "+919509597501",
        "postalCode": "452001",
        "tags": [
            "id magna sed Lorem",
            "Duis dolor commodo aliqua"
        ],
        "website": "https://www.google.com/",
        "attachments": [],
        "assignedTo": "nmFmQEsNgz6AVpgLVUJ0"
    }
    {
        "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
        }
    ]
    {
    	"secret": "AABBCCDD"
    }
    {
    	"baseUrl": "https://www.example.com",
    	"headers": {
    		"Signature": "{{common.secret}}"
    	}
    }
    {
    	"timeout": 300000
    }
    {
    	"url": "https://www.example.com",
    	"method": "GET",
    	"timeout": "{{common.timeout}}"
    }
    inherited from the Base
    :
    base URL
    ,
    error handling
    ,
    log sanitize
    . If something needs to be changed, you can write it here, and it will override the settings of the Base component.
    Static parameters

    Input parameters that the user cannot map from other modules. They are only used for polling triggers, which don't have mappable parameters.

    Mappable parameters

    Input parameters in the interface that the user can either enter manually or map from the output of other modules.

    Interface

    Labels of the module's output added to make the output more straightforward and easy to interpret. By specifying it, there’s no need to first run the module in your scenario to get the output structure for mapping the elements.

    Samples

    Examples of data to help the users set up the module.

    Create a Search module

    To create a new module for your Geocodify app:

    1

    In the Modules tab, click Create Module.

    2

    In the pop-up window, fill in the module details. The chart below contains the values to use for your Geocodify app.

    Field
    Values
    3

    Click Save.

    4

    Click the Mappable Parameters tab for your new search module.

    5

    Copy and paste the following code:

    Mappable parameters
    Description
    6

    Click Save changes.

    7

    Click the Communications tab for your new search module.

    8

    Copy and paste the following code:

    Specification
    Description
    9

    Click Save changes.

    Leave all the other tabs as they are, including the Interface. This means that you won't apply any filtering or customization to the output, but all the information from the API will be returned as is.

    Now you are ready to test your app.

    six different types of modules
    Format (plural)
    Correct
    Incorrect

    Watch [item]s

    Watch contacts

    Watch a contact

    Watch new [item]s

    Watch new contacts

    Watch contacts created

    Watch updated [item]s

    Watch updated contacts

    Watch contacts updated

    Watch deleted [item]s

    Watch deleted contacts

    Watch contacts deleted

    Action modules

    Action modules write data into a service, modify data in a service, or retrieve a single result.

    Module type
    Format (singular)
    Correct
    Incorrect

    Add

    Add a/an [item]

    Add a reaction

    Add a user to a list

    Create

    Create a/an [item]

    Create a message

    Create a completion

    Create or Update

    Create or update a/an [item]

    Create or update a record

    Create or update a vector

    Search modules

    Search modules retrieve data from a service and allow for one or more results.

    Module type
    Format (plural)
    Correct
    Note

    List

    List [item]s

    List users

    List entity types

    List modules are those that have no filtering options.

    Search

    Search [item]s

    Search users

    Search contacts

    Search modules are those that have one or more filtering options.

    Bulk modules

    Bulk modules can perform an action on multiple records in a single call.

    Format (plural)
    Correct

    Bulk [action] [parameter] (advanced)

    Bulk upload call conversions (advanced) Bulk create folders (advanced)

    Additional information

    Some modules will require additional information in the name, such as (advanced) for bulk modules, or (beta). In these cases, the singular adjective should be lowercase and placed between ( ).

    Information type
    Format
    Correct
    Incorrect

    Advanced

    Module name (advanced)

    Search rows (advanced)

    Search rows (Advanced module)

    Beta

    Module name (beta)

    List folder items (beta)

    List folder items (BETA)

    Advanced, beta (both tags)

    Module name (advanced) (beta)

    Update a campaign (advanced) (beta)

    sentence case
    Additional information (when necessary): Include additional information if the module has any requirements, such as a specific plan on the third-party or in Make, or requires a specific setting on the third-party side.

    Watch modules

    Watch modules watch for new data in a service and return it. They are trigger and instant trigger (webhook) modules.

    The general format for trigger module descriptions: Watch [items] Triggers when a/an [item] is ...

    Module name format
    Module description format
    Module name
    Module description

    Watch new [items]

    Triggers when a new [item] is created.

    Watch new contacts

    Triggers when a new contact is created.

    Watch updated [items]

    Triggers when a/an [item] is updated.

    Watch updated contacts

    Triggers when a contact is updated.

    Watch deleted [items]

    Triggers when a/an [item] is deleted.

    Action modules

    Action modules write data into a service, modify data in a service, or retrieve a single result.

    The general format for action module descriptions: &#xNAN;[Action] a/an [item] [Actions] a/an [item] + details …

    Module name format
    Module description format
    Module name
    Module description

    Create a/an [item]

    Creates a new [item]….

    Create a supplier invoice

    Creates a new supplier invoice.

    Create or update a/an [item]

    Creates a new [item] or updates an existing one if a matching [x] is found.

    Create or update a contact

    Creates a new contact or updates an existing one if a matching contact name or email is found.

    Delete a/an [item]

    Deletes a/an [item]…

    Add if specified in API documentation:

    …This action cannot be undone.

    Search modules

    Search modules retrieve data from a service and allow for one or more results.

    The general format for search module descriptions: &#xNAN;[Action] [items]

    Returns a list of [items] + details ...

    Module name format
    Module description format
    Module name
    Module description

    List [items]

    Returns a list of [items]…

    List users

    Returns a list of users in a specific organization.

    Search [items]

    Returns a list of [items] filtered by [?]…

    Search users

    Returns a list of users filtered by [?].

    Universal modules

    Universal modules are modules used to make an API call or query when a pre-built module does not exist.

    The general format for Make an API call module descriptions: Sends a custom API call to {app name}. You can use this to call endpoints that aren’t covered by existing modules.

    Module name format
    Module description format
    Module name
    Module description

    Make an API call

    Sends a custom API call to {app name}. You can use this to call endpoints that aren’t covered by existing modules.

    Make an API call

    Sends a custom API call to Shopify. You can use this to call endpoints that aren’t covered by existing modules.

    Make a SOAP API call

    Make a SOAP API call

    Execute a GraphQL query

    Connections

    When building the Connections component, you will set up the form that appears when the user clicks Create a connection in a module.

    You will define the parameters the user has to input and how these are handled by the apps engine to verify the authentication parameters.

    The Connections component defines the details needed to authenticate with the API and ensures everything works when the user enters their credentials in the scenario editor.

    It has three main jobs:

    1. to get any relevant information from the user that adds the connection (credentials, API key, etc.)

    2. to process the authentication

    3. to check if the authentication works by making a test API call

    You set this up in the Communication tab, while the Parameters tab holds the information the user needs to provide.

    The code in the Communication tab is divided into three sections:

    Request

    The first part defines the HTTP request the app uses to validate the user's credentials.

    This call happens when the user enters their credentials and clicks Create a Connection.

    The usual way to validate credentials is to call an API endpoint that returns user details. If the API doesn't provide this, you can use a generic endpoint to validate the credentials by making a GET request to ensure that the authentication is correct. The default HTTP method is GET, so you don’t need to specify it.

    Adding this validation call is a good practice to make sure the credentials are correct.

    Response

    The second part contains directives on how to handle the response:

    • metadata: Stores the user details that are returned by the API call and displays them next to the connection in the Connections page, for ease of identification.

    • error: Contains instructions on the information that is displayed when an error occurs to help the user understand the issue. Lets you set the type of error and the message that will appear if the API request fails.

    Log

    The third part contains instructions regarding the log and the information recorded in it:

    • sanitize: specifies which information shouldn't be recorded in the logs for security reasons.

    The code in the Parameters tab contains only one parameter: apiKey. This is the information the user has to provide in the scenario. Note that the parameter is used in the Communication tab using the notation {{parameters.name}}.

    Set up connections for your Geocodify custom app

    To set up the connections for your custom app:

    1

    In the Connections component, click Create Connection.

    2

    Fill in the details of your connection.

    • Label: Geocodify API key

    Continue to set up the .

    Processing of output parameters

    Response output

    A module's response output should be defined for the case when a request is fulfilled successfully. The output definition should not 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:

    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. If the response returned is unchanged and if all the parameters still aren't described in the output parameters, Make will automatically suggest additional parameters from incoming data.

    Search modules

    Action modules

    Delete unnecessary output data

    Your output data might include information related to a backend process. While you may use this information for error handling, for example, it's not necessary to pass this information to another service.

    Sending superfluous information might cause confusion. For example, a 'status' value of a task could be easily mistaken for the 'status' of the processing of data.

    To avoid confusion, structure your output in a user-friendly format and delete unnecessary output data.

    Parse dates

    You should always parse datetime in the module output with the following exceptions:

    • No date, only time. For example, 13:30.

    • No time, only date. For example, 2024-01-01.

    • With both date and time, but no time zone. For example: 2020-06-08 12:37:56.

    Examples

    Pipedrive

    Part of the response from Pipedrive API /activities :

    from Pipedrive

    Field
    Parse?
    Reason

    Virtuagym

    Part of the response from Virtuagym API /api/v0/activity:

    Field
    Parse?
    Reason

    Flatten nested outputs

    Deeply nested outputs result in undesirable UX when the user maps the values.

    Consider:

    • creating a mappable version of the key-value pairs, and

    • flattening unnecessary nested collections.

    See an additional example of.

    Error handling

    Handling errors returned from HTTP API endpoints

    Since any API endpoint can return an error during an execution of a scenario, Make provides methods to handle errors and keep scenarios reliable and functional.

    All apps in Make should implement error handling to ensure that users understand the cause of any errors. Error handling should always be customized to the type of error and how the API returns the error.

    Read more about error handling in the Help Center:

    • Introduction to errors and warnings

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

    Error handling: 401 error

    With error handling, details of the error are available and the user will know how to solve the problem.

    HTTP 4xx and 5xx error handling

    When the response returns a 4xx or 5xx HTTP error code, this is automatically considered an error. If the error directive is not specified, the user will only see the status code that was returned. You should customize the message shown to the user with the error or error.message directive.

    HTTP 2xx and 3xx error handling

    Some APIs signal an error with a 200 status code and a flag in the body. In this situation, use the valid directive to tell the user if the response is valid or not.

    Custom error handling based on status codes

    You can further customize what error message will be shown to the user based on the status code. To do this, add your status code to the error directive.

    Available error types

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

    Error type
    Description

    Error handling with type

    Basic connection

    Connection is a link between Make and a third-party service or app.

    Basic connections include different authentication methods that don't need any token exchange mechanism. The most common case uses API keys where you send the key with the request to the endpoint you want to use. Types of authentication supported by basic connections include:

    • API key (or similar single-token auth types)

    • Basic Auth (a username and password pair encoded with base64), for example: "{{base64('user:pass')}}"

    • Digest Auth (a pair of credentials hashed with md5)

    Components

    Communication

    For more information, see the documentation.

    • aws directive is not available

    • Only a single request can be performed

    • pagination directive is not available

    • response.limit

    response.data

    The data directive saves data to the connection so it can be accessed later from a module through the connection variable. It functions similarly to the temp directive, except that data is persisted in the connection.

    This accessToken can be later accessed in any module that uses this connection.

    response.metadata

    The metadata directive allows you to save the user’s name or username (or any other text field) so multiple connections of the same type can be easily recognized. A common practice is to save either username, 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.

    response.uid

    The response.uid directive allows you to save the user’s remote service ID. This is required when using .

    Parameters​

    the user needs to provide when setting up a new connection.

    ​Common data​

    like secrets.

    Available IML variables

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

    Varible
    Description

    API key-based connection example

    Basic Auth connection example

    Batch actions

    A batch action is an operation that lets a user complete more than one action in a module.

    We advise that you do not use batch actions as the Make platform does not support the partial success of a call.

    If your module involves two API calls, it's possible that one will succeed and the other will fail. The module will show an error message for this partial success.

    Instead, we suggest using a separate module for each API call:

    • to avoid receiving misleading or incorrect error messages and,

    Webhooks

    Webhooks are used with instant triggers that execute the flow immediately after the remote server sends data.

    To use webhooks, 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.

    Components

    Communication

    For more information, see the .

    • The communication response is extended with the wrapper object.

    Upsert a record

    Delete

    Delete a/an [item]

    Delete a message

    Delete a user from a list

    Download

    Download a/an [item]

    Download a message

    Download an image

    Generate

    Generate a/an [item]

    Generate an image

    Generate an audio file

    Get

    Get a/an [item]

    Get a message

    Get a user

    Invite

    Invite a/an [item]

    Invite a user

    Invite a user to a channel

    Remove

    Remove a/an [item]

    Remove a reaction

    Remove a user from a list

    Send

    Send a/an [item]

    Send a message

    Send an email to a team member

    Update

    Update a/an [item]

    Update a message

    Update a product variant

    Upload

    Upload a/an [item]

    Upload an image

    Upload a product image

    Update a campaign (advanced, beta)

    Deprecated

    Module name (deprecated)

    Send a message (deprecated)

    Send a message (Deprecated)

    Rebrand

    Module name (formerlyl [name])

    X (formerly Twitter)

    X (Formerly Twitter)

    Watch deleted contacts

    Triggers when a contact is deleted.

    Watch [items]

    Triggers when a/an [item] is created or updated.

    Watch contacts

    Triggers when a contact is created or updated.

    Watch [items]

    (for events)

    Triggers when an event occurs related to a/an [item].

    Watch records

    Triggers when an event occurs related to a record.

    Watch events

    Triggers when an event occurs [optional - location or time].

    Watch events

    Triggers when a new event occurs on Monday.

    Delete a message

    Deletes a message from a thread. This action cannot be undone.

    Download a/an [item]

    Downloads a/an [item]…

    Download an document

    Downloads a document in PDF format.

    Get a/an [item]

    Returns information about a specific [item] by its [x].

    Get a user

    Returns information about a specific user by their user ID.

    Move a/an [item] to trash

    Moves a [item] to the trash.

    Move a file or folder to trash

    Moves a file or folder to the trash.

    Redact a/an [item]

    Redacts [information] for a/an [item]…

    Redact a contact

    Redacts all personally identifiable information for a contact, but does not delete the contact record itself.

    Update a/an [item]

    Updates a/an [item]…

    Update a message

    Updates a message.

    Add [items] to a/an [item]

    *Adding multiple items to a single item

    Add [items] to a/an [item]…

    Add members to a list

    Adds members to a specified list.

    Bulk [verb] [items]

    [Verbs] multiple [items]…

    Bulk add rows

    Adds multiple rows to the bottom of a table.

    Execute a GraphQL query

    Instructions for the user displayed in the module setup. It supports Markdown for text formatting.

    required

    Specifies if the parameter is required.

    Defines the output returned, which in this case is the whole body of the response.

    Template

    Blank module (you will set it up from scratch)

    Type

    Search (to retrieve geolocation details)

    Connection

    Geocodify API Key (the connection you have created earlier)

    Name

    geocode

    Label

    Search geolocation

    Description

    Provides longitude, latitude, and place details of a location (address, name of a place, or location).

    name

    Required. Internal name of the parameter. Use it when you want to access the parameter using {{parameters.name}}.

    label

    Parameter name displayed in the module setup.

    type

    Required. Type of the parameter.

    url

    The API endpoint. Since it starts with /, it is joined to the base URL. Note that if the URL starts with https://, it will override the base URL.

    method

    The default method is GET, so it could have been omitted in this case.

    qs

    Query parameter containing the location to geolocate. Note that you access it by using parameters.location_info.

    help

    response

    Exception: If the API documentation specifies that the given datetime is in UTC or some other time zone.

  • Do not use the Make user’s time zone or the Make organization’s time zone, because it may be different from the time zone configured in the third-party services.

  • Time stamp in seconds or milliseconds.

    • Exception: If it has a clear indication by its name, in the API documentation or metadata endpoints, that shows such fields are a Date type field. DO NOT try to parse time stamps by assuming a lengthy number must be a time stamp.

  • marked_as_done_time

    Yes

    Only date and time with no time zone.

    BUT we know it’s UTC.

    update_time

    Yes

    Only date and time with no time zone.

    BUT we know it’s UTC.

    due_date

    No

    No time, only date.

    due_time

    No

    No date, only time.

    duration

    No

    No date, only time.

    add_time

    Yes

    Only date and time with no time zone.

    BUT we know it’s UTC.

    timestamp_edit

    Yes

    Timestamp in seconds with clear indication by it’s name.

    timestamp

    Yes

    Timestamp in seconds with clear indication by it’s name.

    API documentation: Date format
    implementing dynamic mappable parameters using a custom IML function

    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 and 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.

    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.

    Overview of error handling
    Error handlers
    Error in Make DevTool
    Error with no detail message
    Error with a detail message
    is not available
  • response.iterate directive is not available

  • response.output is not available

  • response is extended with data , uid and metadata

  • now

    Current date and time

    environment

    TBD

    temp

    Contains custom variables created via temp directive.

    parameters

    Contains the connection’s input parameters.

    common

    Contains the connection’s common data collection.

    data

    Contains the connection's data collection.

    communication
    shared webhooks
    Parameters
    Non-user-specific sensitive values
    Example of a connection's name
    /// Defines "location" as module input parameters.
    [
        {
            "name": "location_info", // Makes value accesible via "{{parameters.location_info}}".
            "label": "Location", // Sets the user friendly label visible in the module
            "type": "text", // Sets the type to text.
            "help": "Type the location you want to geolocate", // Sets the help text with an example under input field in the module.
            "required": true // Indicates this parameter is mandatory.
        }
    ]
    {
    	// Request to API endpoint.
    	"url": "/geocode", // Endpoint relative to base URL
    	"method": "GET",
    	"qs": {
    		"q": "{{parameters.location_info}}" // Required query parameter. Parameter "location_info" is defined in Mappable parameters. 
    	},
    
    	// Response handling
    	"response": {
    		"output": "{{body}}" // Return the body of the response
    	}
    }
    "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}}"
        }
    }
    [
        {
            "id": 8,
            "due_date": "2022-06-09",
            "due_time": "10:00",
            "duration": "01:00",
            "add_time": "2020-06-08 12:37:56",
            "marked_as_done_time": "2020-08-08 08:08:38",
            "update_time": "2020-08-08 12:37:56"
        }
    ]
    {
        "act_inst_id": 25957,
        "timestamp_edit": 1319816131,
        "timestamp": 1319796000
    }
    {
        // ...
        "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}}"
            }
        }
    }
    {
        "response": {
            "data": {
                "accessToken": "{{body.token}}"
            }
        }
    }
    {
        "url": "http://example.com",
        "headers": {
            "X-API-Key": "{{connection.accessToken}}"
        }
    }
    ...
    "response": {
    	"metadata": {
                "type": "email",
                "value": "{{body.data.user.email}}"
            }
    	},
    ...
    {
        "response": {
            "uid": "{{body.data.id}}"
        }
    }
    [
        {
            "name": "apiKey",
            "type": "password",
            "label": "API key",
            "advanced": true,
            "editable": true
        }
    ]
    {
    	"url": "https://example.com/api/v1/info",
    	"method": "GET",
    	"headers": {
    		"X-API-Key": "{{parameters.apiKey}}"
    	},
    	"response": {
            "valid": "{{!body.error && statusCode === 200}}",
    		"error": {
    			"message": "[{{statusCode}}] {{body.error}}"
    		},
            "metadata": {
                "type": "email",
                "value": "{{body.user.email}}"
            }
    	},
    	"log": {
    		"sanitize": ["request.headers.`X-API-Key`"]
    	}
    }
    [
        {
            "name": "username",
            "type": "text",
            "label": "Username",
            "advanced": true,
            "editable": true
        },
        {
            "name": "password",
            "type": "password",
            "label": "Password",
            "advanced": true,
            "editable": true
        }
    ]
    {
    	"url": "https://example.com/api/v1/info",
    	"method": "GET",
    	"headers": {
    		"authorization": "Basic {{base64(parameters.username + ':' + parameters.password)}}"
    	},
    	"response": {
            "valid": "{{!body.error && statusCode === 200}}",
    		"error": {
    			"message": "[{{statusCode}}] {{body.error}}"
    		},
            "metadata": {
                "type": "email",
                "value": "{{body.user.email}}"
            }
    	},
    	"log": {
    		"sanitize": ["request.headers.authorization"]
    	}
    }

    Type: API Key

    3

    Click Save.

    4

    Select the Parameters tab and remove the default parameter that is present.

    5

    Copy and paste the following code:

    Parameter
    Description

    name

    Required. Internal name of the parameter. Use it when you want to retrieve the parameter

    label

    Parameter name as displayed in the module.

    type

    Required. Data of the parameter.

    help

    6

    Click Save changes.

    7

    In the Communication tab, remove the code present.

    8

    Copy and paste the following code:

    Code
    Description

    "url": "https://api.geocodify.com/v2/geocode", "qs": { "api_key": "{{parameters.apiKey}}" },

    Request that the apps engine makes to validate the credentials.

    • url: Absolute URL of the endpoint that is used for validation.

    • qs: Query string.

    • api_key: Key of the qs parameter as specified by the API docs. This means that it will use the apiKey that the user provides.

    "response": { "error": { "message": "[{body.meta.code}}] {body.meta.error_detail}}" } },

    Instructions on how to display any error: [error code] error message.

    This information is typically present in the API docs. Since it isn’t available in this case, you need to retrieve it manually.

    Send a request with incorrect credentials in Postman, then check the response body to identify where the error code and message appear.

    If available, it's good practice to include the status code in the error response.

    "log": { "sanitize": [ "request.qs.api_key" ] }

    Indicates to omit the api_key parameter present in the query string of the request from the log.

    9

    Click Save changes.

    Base
  • to allow users to set up error handling for individual modules.

  • Exceptions

    The following are exceptions when you may want to use a batch action:

    • PUT vs PATCH behavior in update modules

    • Get a record after update

    • Upload a file

    • Download a file

    • \

    PUT vs PATCH behavior in update modules

    Generally, PUT does not support partial updates, meaning you need to provide a full request to avoid losing the rest of the record. PATCH supports partial updates.

    However, in practice, many APIs with PUT methods support partial updates.

    Make expects empty fields to be ignored, not erased.

    Chain of actions: Read > Write

    1. Get record by ID

    2. PUT the record patched by user’s input

    Example

    • GoHighLevel > Update a Contact

    If a partial update is not supported, you must execute an extra call to retrieve the current data and combine the data using an IML function.

    If a value needs to be deleted, use the erase pill (available only in update modules).

    Get a record after update

    Some services only return an ID after a record update.

    Chain of actions: Write > Read

    1. Update the record

    2. Get the record by ID

    Example

    • Workday Financial Management > Update a Supplier Invoice

    Upload a file

    Upload in chunks

    Chain of actions: Write > Write > … > Write

    This is an exception to the rule that combines multiple Write actions. These requests are designed to be used in sequence.

    1. Create an upload session

    2. Keep sending chunks

    3. Finalize the upload

    Example

    • Dropbox > Upload a File

    Download a file

    Download by media ID

    Chain of actions: Read > Read

    1. Get the media ID

    2. Download file by media ID

    Example

    • WhatsApp Business Cloud > Download a Media

    • Telegram Bot > Download a File

    Asynchronous process

    Requests involving processes running asynchronously on 3rd party service.

    Chain of actions: Write > Read > Read > … > Read

    1. Start the task

    2. Keep polling for task status

    3. Get the result when done

    Example

    • Microsoft Power Automate: Trigger a Desktop Flow

    Responsiveness approaches

    Keep in mind that there are two approaches to responsiveness in a service:

    • Synchronous - Upon an action request, the service returns a result that 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. The result can be processed in the following modules.

    Helpful when you need to process a large amount of data, like a file conversion.

    Disadvantage

    The job may take too long. This might cause a timeout (default 40 sec). For example, in a file conversion. The default timeout can be prolonged depending on the valid cases.

    You need to create at least two scenarios - one for triggering the job, and another one for processing 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.

    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. There should be two (or more calls) executed instead of only one:

    1. Create a call

    2. Check the status of the call

    3. Request the result of the call

    Example

    After importing a JSON file to a web service, it requires a certain period of time to process the file. In this case, continue to check if the status of the entity changed from processing to completed. 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.

    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

    respond

    Response specification

    Specifies how to respond to the remote server.

    verification

    Verification specification

    Specifies how to reply to the remote server. Used for webhooks that require a verification mechanism, such as challenge responses.

    iterate

    IML string or iterate specification

    Specifies how the response items (in case of multiple) are retrieved and processed.

    output

    Any IML type

    Describes the structure of the output bundle.

    respond

    Required: no

    This directive lets you customize Make's response on the webhook or a verification request.

    Key
    Type
    Required
    Description

    type

    IML string

    no

    Specifies how to encode data into the 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

    verification

    Required: no

    This directive allows you to reply to webhook verification requests. Some systems issue a verification request during webhook creation to ensure your webhook is prepared to handle incoming data. Such systems may send a code and request Make to return it and maybe some other value with it. In such case, this directive will help you.

    Key
    Type
    Description

    condition

    IML string

    Specifies when to treat the incoming data as a verification request.

    respond

    IML string

    Specifies the response.

    verification.condition

    Required: no

    Default: true

    This directive distinguishes normal webhook requests from verification requests. Usually, the remote service will send some code to verify that Make is capable of receiving data. In such cases, you want to check for the existence of this code variable in the request body. If it exists, this request is a verification request. Otherwise, it would be a normal webhook request with data.

    verification.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 the normal respond.

    iterate

    Properties of the iterate directive are described in the communication documentation.

    output

    Properties of the output directive are described in the communication documentation.

    condition

    Properties of the condition directive are described in the communication documentation.

    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. Remember 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:

    Variable
    Description

    now

    Current date and time

    environment

    TBD

    parameters

    Contains the 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.

    Types of webhooks

    Shared

    Shared webhooks are used when the external service sends all events from all users to a single URL that you control. With this, the user is not able to see the URL and you must use the uid in the connection and in the webhook communication to associate the payloads to the right users.

    Dedicated

    ​Dedicated webhooks are the most common type. An individual URL is created and either automatically registered to the external platform or the user may need to configure it manually. The user can see the URL and copy it from the scenario.

    {
        "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
    }

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

  • Communication can be request-less.

  • 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

    The action module should always output only one bundle.

    Samples

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

    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:

    Variable
    Description

    now

    Current date and time

    environment

    TBD

    temp

    Contains custom variables created via the temp directive.

    parameters

    Contains the module’s input parameters.

    connection

    Contains the connection’s data collection.

    common

    Contains the app’s common data collection.

    Additional variables available for the response object:

    Variable
    Description

    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:

    Variable
    Description

    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 for pagination and response objects:

    Variable
    Description

    body

    Contains the body that was retrieved from the last request.

    headers

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

    items

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

    Action module example

    communication documentation

    Parameter hints

    Hints play a very important role in the overall usability of an app and well-written hints can have a significant positive impact on the user experience.

    When providing hints, take into consideration the following:

    • All fields that are not self-explanatory should contain a hint

    • Extra attention should be paid to Connection fields

    • All essential information should be included

    • The information should be clear and concise

    • The terminology used should be non-technical and easy to understand

    • Avoid using symbols

    Information to include

    There are six categories of information that hints can include. Each hint can contain at least one of the information types. If multiple information types are included, they should be organized in the order listed below.

    1. Expected input

    2. Result

    3. Example

    4. Additional information

    Information type
    Description
    Example

    Connection field hints

    API key / API token / Access token

    The name of the field should always match what the user sees in the 3rd party UI.

    Where to direct users
    Format
    Example

    Required client ID and client secret

    The process to obtain these values should be documented in our Help Center if the fields are required.

    Where to direct users
    Format
    Example

    Optional client ID and client secret in advanced settings

    The process to obtain these values do not need to be documented in our Help Center as the fields are not required.

    Where to direct users
    Format
    Example

    Limit field hints

    Location
    Field
    Format
    Link

    The Limit field should also be the last standard field in the module. It should not be in the advanced settings.

    Make an API call: URL field hints

    If a hint includes a URL, the URL should contain the prefix path and an example of the URL. Additionally, the URL field hint should include an example endpoint that works without performing any additional steps (for example, where no {body} is required).

    Use GET methods as the example endpoints. Do not use endpoints that create or delete records.

    Do not hard code API versions in the prefix path. This ensures a user can work with any API version. Even if there is currently only one version, future compatibility should be considered.

    Field
    Format
    Example

    Special formatting

    Two types of special formatting are used in hints: bold and code.

    If a hint references another input field in the module, make sure to copy the input field’s name exactly and format it in bold.

    If you include examples, default values, versions, or specific formats, make sure to use the red code formatting. This can include API versions, color codes, dates, time, and country codes.

    Manage breaking changes

    When you make changes in an app, make sure your changes will not break existing scenarios.

    Ideally, the removal of the connections, modules, and fields should be announced to the users in advance.

    Contact the help desk with a request for email notification to users.

    Common breaking changes

    App's Element or Block
    Breaking Changes

    Remove a parameter from a module

    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. Add the [Deprecated] string to 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.

    1. Add 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.

    1. Perform 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.

    Shut down a module

    If you need to make sure that the module is not used anymore, you can throw an error whenever the module is executed.

    Deprecate a connection

    If you need to deprecate a connection, create a new connection to use as the functional alternative and rename the now-deprecated connection so it 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.

    Processing of input parameters

    Mapping all parameters

    In this example you have to map every parameter correctly.

    There is a risk of a typo or omission of a parameter.

    OAuth 1.0

    Connection is a link between Make and a third-party service or app. The OAuth 1.0 connection handles the token exchange automatically.

    While OAuth 1.0 is supported, it is not commonly used. Unless you are dealing with a legacy platform, we suggest you use a or .

    Before you start to configure your OAuth 1.0 connection, create an app on a third-party service. When creating an app, use https://www.integromat.com/oauth/cb/app-oauth1 as a callback URL.

    Components

    [
        {
            "name": "apiKey",
            "label": "API Key",
            "type": "password",
            "help": "Enter the API Key provided by Geocodify. For details, see the  [Geocodify account documentation](https://geocodify.com/account).",
            "required": true,
            "editable": true
        }
    ]
    {
    	// Request
    	"url": "https://api.geocodify.com/v2/geocode", // Absolute URL to the API endpoint which validates credentials
    	"qs": { // Query parameters
    		"api_key": "{{parameters.apiKey}}" // Authorizes user by api key, provided by user during the connection creation.
    	},
    	"response": {
    
    		"error": { // Error handling
    			"message": "[{body.meta.code}}] {body.meta.error_detail}}" // On error, returns error details
    
    		}
    	},
    	"log": {
    		"sanitize": [ // Excludes sensitive parameters from logs.
    			"request.qs.api_key" ] // Omit query string apy_key
    	}
    }
    {
        {
        "url": "/webservice.php", 
        "method": "GET",
        "qs": {
            "id": : "{{parameters.object}}",
            "operation": retrieve"
            },
             "response": {
                 "temp": {
                     "fields": "{{body.results[]}}"
                 }
             }
        },
         {
             "url": "/webservice.php",
             "method":"POST",
             "type": "urlencoded", 
             "body": {
                 "operation": "revise",
                 "elementType": "{{parameters.elementType}}", 
                 "element": "{{stringifyFields(parameters, 'revise', temp.fields)}}
         },
         "response": {
             "output": "{{objectResponse(body.result)}}"
         }
     }
     }
             
    {
        "last_name": "Smith",
        "contact_id" : "12345",
        "first_name" : null,
        "company_name" : "Acme Company"
    }
    [
    	{
    		"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)}}"
    		}
    	}
    ]
    {
        "verification": {
            "condition": "{{if(body.code, true, false)}}",
            "respond": {
                "status": 202,
                "type": "json",
                "body": {
                    "code": "{{body.code}}"
                }
            }
        }
    }
    {
    	"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
    }

    condition

    IML string or boolean

    Determines whether to execute the current request.

    uid

    IML string

    Specifies how to get the user ID from the request body. Necessary to associate the recipient when using a shared webhook.

    Specifies custom headers that are to be sent with the response.

    body

    Any IML type

    no

    Specifies the response body.

    method

    Contains HTTP method of an incoming webhook.

    headers

    Contains headers of an incoming webhook.

    Instructions for the user displayed in the module setup. Supports Markdown for text formatting.

    required

    Specifies if the parameter is required.

    editable

    (Only for the connection) Specifies if the user can edit and modify the connection from the Connections page in Make.

    type

    Create an archive job in CloudConvert

    Download files from the job in CloudConvert

    The first scenario contains Create a Job (advanced) module which has asynchronous approach by default. The only result is the job's ID.

    Asynchronous process
    CloudConvert > Convert a File
    CloudConvert > Create a Job (advanced)
    Convert files from Google Drive in CloudConvert
    Convert a File

    data

    Contains the module’s data collection.

    scenario

    TBD

    metadata.expect

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

    metadata.interface

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

    Base

    Any changes might break scenarios.

    Connection

    Changing the refresh call for OAuth connection.

    Module's Communication

    Changing response.output.

    Changing response.type.

    Adding response.valid for 2xx response.

    Changing response.trigger.

    Adding a condition.

    Adding an additional call (chaining request).

    Changing a 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 type coercions.

    Adding validate.

    Setting the select parameter mappable to false.

    Setting the 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.

    [
        {
            "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."
        }
    ]
    {
        "type": "banner",
        "title": "This is an info",
        "text": "Here is a description of info.",
        "theme": "info"
    }
    {
    	"type": "banner",
    	"title": "This is a warning",
    	"text": "Here is a description of warning.",
    	"theme": "warning"
    }
    {
    	"type": "banner",
    	"title": "Headline, if needed...",
    	"text": "Minimum recommended length of message text. (50 characters)",
    	"theme": "danger"
    }
    [
    	{ // when parameter name exists
    	"condition": "{{parameters.name}}", 
            "response": {
                "valid": {
                    "condition": false, //to throw response as error
    		"type": "DataError",
                    "message": "You can't 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}}"
    		}
    	}
           
    ]
    {
            "response": {
                "valid": {
                    "condition": "{{'a' === 'b'}}",
                    "message": "You can't use this module anymore as it has been shut down.\nPlease read the module's note message."
                }
            }
      }
    What if left empty
  • Link

    1. What if left empty

    Describe the impact of not entering a value.

    Format

    If left empty, default formatting is used.

    1. Link

    When linking to Make's Help Center, use ‘our Help Center’. When linking to third-party documentation, include the name of the app/service and the name of the page/documentation.

    Account

    Name of the primary user’s account. For details, see our . Voice

    Voice to use in the audio. For voice samples, see the .

    Third-party account:

    with instructions *not preferred due to length

    You can obtain your [name of value] by going to [item]→ [item] → [item] in your [app name] account

    You can obtain your refresh token by going to Account → Profile → API in your Atlassian account.

    1. Expected input

    Include a clear description of what to enter/select. This will often be the description included in the API documentation.

    If the description is not clear or is too technical, update it to be more user-friendly.

    Response format

    Format of the generated audio file.

    1. Result

    Include a clear description of the outcome, especially if there are various possible outcomes. Include this information if it is useful to describe what will happen when users enter a specific value.

    Temperature

    Higher values generate a more random response. For example, 0.8. Lower values generate a more focused response. For example, 0.2.

    1. Example

    Include an example to provide more clarity, if specific formatting is used, or if it is valuable for users. Use the format ‘For example, code formatting’.

    Image URL

    URL address to a public resource of the image. For example, https://getmyimage.com/myimage.png.

    1. Additional information

    Include extra information the user must know to successfully use the field.

    Our Help Center

    For details on how to obtain your [name of value], see our Help Center.

    For details on how to obtain your API key, see our Help Center. Link to apps.make.com/[your-app-slug]

    Third-party resource:

    API docs

    For details on how to obtain your [name of value], see the [app name] API documentation.

    For details on how to obtain your API key, see the Instantly API documentation.

    Third-party resources:

    other than API docs

    For details on how to obtain your [name of value], see the [app name] [page name].

    For details on how to obtain your access token, see the Shopify App Development blog.

    Third-party account:

    with link

    You can obtain your [name of value] on the [page name] in your [app name] account.

    You can obtain your [name of value] on the [app name] [page name].

    Our Help Center

    For details on how to obtain your [client ID or client secret], see our Help Center.

    For details on how to obtain your client secret, see our Help Center. Link to apps.make.com/[your-app-slug]

    Third-party resource:

    API docs

    For details on how to obtain your [client ID or client secret], see the [app name] API documentation.

    For details on how to obtain your client secret, see the Hotmart API documentation.

    Third-party resource:

    other than API docs

    For details on how to obtain your [client ID or secret], see the [app name] [page name].

    For details on how to obtain your client ID, see the Oracle Help Center.

    Polling trigger

    Limit

    Maximum number of results to return. For information about setting limits, see our Help Center.

    https://help.make.com/types-of-modules#b3ZEQ

    Search and List modules

    Limit

    Maximum number of results to return and work with during one execution cycle. For information about setting limits, see our Help Center.

    https://help.make.com/types-of-modules#b3ZEQ

    URL

    Enter the part of the URL that comes after [prefix path]}. For example, [postfix].

    Enter the part of the URL that comes after https://api.openai.com. For example, /v1/models.

    Output file name

    Name of the generated audio file. Do not include the file extension.

    You can obtain your refresh token on the in your Atlassian account.

    You can obtain your API key on the .

    {
        // ...
        "body": "{{parameters}}",
        // ...
    }

    With this approach, all parameters from the modules will be sent to the API.

    [
        {
            "name": "email",
            "type": "email",
            "label": "Email address",
            "required": true
        },
    
    

    Using an IML function or omitting a parameter

    Sometimes, you don't want to map all parameters in the body. Some reasons may include:

    • 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.

    In this example you have to map every parameter correctly.

    There is a risk of a typo or omission of a parameter.

    Note 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.

    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

    It isn't possible to send a null value to a service.

    It isn't possible to send a null, empty string, and 0 (zero) value to a service.

    It isn't possible to send an empty array to a service. E.g. User wants to remove all tags from a task.

    Let users decide which 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.

    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 don't need to escape them manually.

    The same automatic encoding applies to the headers and body collections for request headers and body payloads, respectively.

    This will issue the request to URL in this way:

    http://yourservice.com/api/items?limit=100&since=2023-01-01&until=2023-01-31

    If you provide a query string directly in the url directive, it won't be automatically encoded. You have to encode it manually. But in common cases, entering the query string in the url is not a recommended approach, especially when values are inserted dynamically. See the Special case: Query string parameters in the URL section for more details.

    Special case: Query string parameters in the URL

    In most cases, having the query string in the URL is not the best practice. It is better to use the qs collection, but sometimes there may be a special case 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 third-party service requires a very specific format or encoding of the query string parameters. In that case, you can add a query string directly to the url directive string and manage the encoding yourself.

    Example of specific query string parameters in the URL:

    The most common use case for this is when a third-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.

    This will issue the request to URL in this way:

    http://yourservice.com/api/items?anytag=one&anytag=two&anytag=three

    Using structured data in qs

    qs (and also headers) are single-level collections only, meaning that you cannot specify a nested collection in their parameters.

    This example will not work.

    Since there is no defined standard for how to encode nested objects in a query string, you need to implement it manually based on special requirements of the third-party service. The most common approach is to use dot notation (use a . to separate the keys in the query string) for nested properties.

    This will issue the request with the query string:

    ?someProp.anotherOne.and-one-more=THIS%20WILL%20WORK

    {
        ...
        "body": {
            "firstName": "{{parameters.firstName}}",
            "lastName": "{{parameters.lastName}}",
            "email": "{{parameters.email}}"
        },
        // ... other directives
    }
    Communication

    For more information, see the communication documentation.

    • 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 tedious and difficult to generate an OAuth 1.0 Authorization header. 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.

    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.

    This accessToken can be later accessed in any module that uses this connection.

    ​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 secrets.

    OAuth 1.0 authentication process

    The OAuth 1.0 authentication process consists of multiple steps. You can fill in the necessary sections and delete those that are unnecessary.

    Key
    Type
    Description

    oauth

    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 the authorization process.

    accessToken

    Request specification

    Describes a request that exchanges credentials and the request token for the access token.

    When using an OAuth 1.0 connection there is a special object available globally: the oauth object. You can use it in the connection specification as well as in module specifications 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 the 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:

    Variable
    Description

    now

    Current date and time

    environment

    TBD

    temp

    Contains custom variables created via temp directive.

    parameters

    Contains the connection’s input parameters.

    common

    Contains the connection’s common data collection.

    data

    Contains the connection's data collection.

    basic connection
    OAuth 2.0

    Search

    The search module 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

    For additional information, see our documentation.

    Pagination

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

    Static Parameters

    You can use inside the search module without any restrictions.

    Mappable Parameters

    You can use inside the search module without any restrictions.

    Interface

    Unlike the action module, the search module can .

    Samples

    To help the users with setting up your module, provide .

    ​Scope​

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

    Available IML variables

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

    Variable
    Description

    Additional variables available for the response object:

    Variable
    Description

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

    Variable
    Description

    Additional variables available for pagination and response objects:

    Variable
    Description

    Example

    {
        "url": "/contact/{{parameters.id}}",
        "method": "PUT",
        "body": {
            "email": "{{parameters.email}}",
            "firstName": "{{parameters.firstName}}",
            "lastName": "{{parameters.lastName}}",
            "registrationDate": "{{formatDate(parameters.registrationDate, 'YYYY-MM-DD')}}"
            
        },
        // ...
    }
    {
        "url": "/contact/{{parameters.id}}",
        "method": "PUT",
        "body": {
            "{{...}}": "{{omit(parameters, 'id', 'registrationDate')}}",
            "registrationDate": "{{formatDate(parameters.registrationDate, 'YYYY-MM-DD')}}"
        },
        // ...
    }
    {
        "url": "/messages.json",
        "method": "POST",
        "body": {
            "status": "active",
            "content": "{{ifempty(parameters.content, undefined)}}"
        },
        // ...
    }
    {
        "url": "/messages.json",
        "method": "POST",
        "body": {
            "status": "active",
            "content": "{{if(parameters.content, parameters.content, undefined)}}"
        },
        // ...
    }
    {
        "url": "/tasks.json",
        "method": "POST",
        "body": {
            "status": "active",
            "tags": "{{if(length(parameters.tags) > 0, parameters.tags, undefined)}}"
        },
        // ...
    }
    {
        "url": "http://yourservice.com/api/items",
        "qs": {
            "limit": 100,
            "since": "{{parameters.since}}",
            "until": "{{parameters.until}}"
        }
        // ... other directives
    }
    {
        "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",
        // ...
    }
    {
        "url": "http://yourservice.com/api/items",
        "qs": {
            "anytag": ["one", "two", "three"]
        }
    }
    {
        "qs": {
            "person": {
                "address": {
                    "street": "123 Main Street"
                }
            }
        }
    }
    {
       "qs": {    
         "person.address.street": "changethis"
        }
    }
    {
        "response": {
            "data": {
                "accessToken": "{{body.token}}"
            }
        }
    }
    {
        "url": "http://example.com",
        "qs": {
            "token": "{{connection.accessToken}}"
        }
    }

    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

    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.

    oauth.scope

    Contains an array of scope required to be passed to the OAuth 1.0 authorization process.

    oauth.redirectUri

    Contains the redirect URL for the OAuth 1.0 authorization process.

    {
    "name": "firstName",
    "type": "text",
    "label": "First Name",
    "required": true
    },
    {
    "name": "lastName",
    "type": "text",
    "label": "Last Name",
    "required": true
    }
    ]

    data

    Contains the module’s data collection.

    scenario

    TBD

    metadata.expect

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

    metadata.interface

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

    now

    Current date and time

    environment

    TBD

    temp

    Contains custom variables created via the temp directive.

    parameters

    Contains the module’s input parameters.

    connection

    Contains the connection’s data collection.

    common

    Contains the app’s common data collection.

    output

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

    limit

    When using a limit, the process of retrieving items will stop once 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.

    iterate.container.first

    Represents the first item of the array you iterated.

    iterate.container.last

    Represents the last item of the array you iterated.

    body

    Contains the body that was retrieved from the last request.

    headers

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

    items

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

    communication
    pagination directive
    static parameters
    mappable parameters
    return multiple bundles at once
    samples
    scope
    [
        {
            "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"
        }
    ]
    {
    
    	"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 to return and work with during one execution cycle.",
    		"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"
    }
    OpenAI Voice options guide
    Security page
    Anthropic Console API keys page

    Trigger (polling)

    The trigger module is a special module that saves the information about the last item processed and continues the execution from that item.

    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 or updated.

    Components

    Communication

    The communication response is extended with the trigger object.

    response.trigger

    The trigger collection specifies directives that control how the trigger works and how your data is processed.

    Key
    Type
    Description

    type

    Date or ID

    Specifies how the trigger will behave and sort items

    order

    Asc or desc

    Specifies in what order the remote API returns items

    id

    IML string

    Must return the current item’s Id

    date

    IML string

    When used, must return the current item’s date

    response.trigger.type

    Required: yes Values: id or date

    This 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 method should be specified in the trigger.date directive. The trigger sorts all items by their date and id fields and returns 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 method should be specified in the trigger.id directive.

    response.trigger.order

    Required: yes Values: asc, desc or unordered

    This 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.

    If the API returns items in ascending order (low to high), then asc should be used. If the API returns items in descending order (high to low), then desc should be used. If the API returns items in no specific order, then unordered should be used.

    When specifying the trigger's communication, sort the results in descending order.

    Make's limit is a return of 3200 records.

    If you sort results in ascending order and the user has more than 3200 records, the trigger won't be able to fetch the latest records. We do not recommending using ascending order for polling triggers.

    response.trigger.id

    Required: yes

    This directive specifies the item’s id. It must always be present.

    For example, if the item looks like this:

    then specify the trigger.id directive like this: {{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 the item looks like this:

    Then specify the trigger.date directive like this: {{item.created_date}}, and the trigger collection might look something like this:

    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, provide samples.

    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:

    Variable
    Description

    now

    Current date and time

    environment

    TBD

    temp

    Contains custom variables created via the temp directive.

    parameters

    Contains the module’s input parameters.

    connection

    Contains the connection’s data collection.

    common

    Contains the app’s common data collection.

    Additional variables available for the response object:

    Variable
    Description

    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:

    Variable
    Description

    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/

    Additional variables available for pagination and response objects:

    Variable
    Description

    body

    Contains the body that was retrieved from the last request.

    headers

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

    items

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

    Example

    {
        "id": 24,
        "name": "Fred",
        "friend_count": 5
    }
    {
        "response": {
            "trigger": {
                "id": "{{item.id}}"
            }
        }
    }
    {
        "id": 24,
        "name": "Fred",
        "friend_count": 5,
        "created_date": "2017-07-05T13:05"
    }
    {
        "response": {
            "trigger": {
                "id": "{{item.id}}",
                "date": "{{item.created_date}}"
            }
        }
    }
    "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}}"
        }
    }
    {
    	"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"
    }

    data

    Contains the 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 the module’s raw parameters array in the way you have specified it in the configuration.

    metadata.interface

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

    Help Center

    Mappable parameters

    A mappable parameter is a variable or setting that you can change or link (map) to another value.

    Types of fields

    You can use mappable parameters in many types of fields:

    Standard fields
    • Visible by default

    • Frequently used

    • Typically easy to understand

    • Visible on the third-party’s Web UI by default

    • Consider copying the UX from the third-party’s Web UI, for clarity

    • Limit should be the last variable

    Advanced fields
    • Hidden by the Advanced settings toggle

    • Not needed by the majority of users

    • Generally more difficult to understand

    Required fields
    • Require minimal effort to make the module work

    • Validate if the user has filled in all necessary fields required by the API call

    • Guard against getting an API error from the third party due to missing fields

    Conditionally-required fields
    • Required based on a condition. For example:

      • Either field A or field B is required

    Optional fields
    • Not necessary for the API to work, but good to have

    • Enrich the UX by providing flexibility to the user to send all fields the API supports

    Static, dynamic, and semi-dynamic fields

    When mapping parameters, there are different types of fields to choose from.

    Static fields

    With static fields, all parameters are manually specified in the module, so every users sees the same options. Though not ideal, some APIs require the use of static fields.

    Dynamic fields

    With dynamic fields, all parameters are loaded dynamically from the API, using . The options shown to a user are based on the user's account specifications. For example, a user who is a manager may have access to more files and folders in an account than another employee. Every user will have a different experience interacting with the module based on their account login. Whenever possible, using dynamic fields is the ideal approach.

    Sometimes field types in third-party applications do not match types supported in Make. In this case, when defining mappable parameters using an RPC, you also need to

    Custom fields

    Custom fields are user-defined fields in the third-party application. You can use custom fields with RPCs as well, for further customization.

    However, if an API supports implementation of custom fields where the field name and field value may be specified by the user (the user is creating new custom fields), both of the fields should not be mandatory. There may be reasons for a value to be empty, and requiring both could cause an error. Instead, require only field value. Additionally, if the API doesn't allow empty values, the validation of an empty value should be set so the pair will not be sent.

    Date parameters

    Use the date and time type parameters instead of asking users to format the date and time themselves. These values should be formatted in the backend to support Make's format.

    Exception: If the endpoint accepts date only or time only, use the text parameter with a clear hint and example.

    Processing of date parameters

    When a field is a type "date", it should be possible to use our keyword "now" as a value. 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. Apps that require this will not be approved by Make.

    Communication:

    Parameter birthday is required to have format YYYY-MM-DD and parameter due_date is required to be a time stamp by the service, so the formatting happens inside the Communication part of the module.

    Parameters:

    Support of array aggregators

    If the API doesn't support arrays or arrays of collection, you need to implement an IML function that enables the use of array aggregators.

    Remote procedure calls (RPCs)

    For every parameter where options are listed dynamically (values pulled from the API), you should implement an RPC, especially when you need to provide an ID (or raw value) instead of a label.

    For example, if you have a lot of customers and you don’t remember their IDs, the RPC will display a list of names / emails and fill in the ID for you. Also, RPCs help the user to understand the behavior and outputs of the module before building the logic of their scenario.

    If there are many RPCs nested to each other, you need to implement an additional select which allows users to choose whether they will map the deepest parameter from previous modules or whether they will follow every RPC to select every parameter in order to get into the deepest parameter.

    Edit mode

    The edit mode ("mode": "edit") is used in modules where the RPCs should be switched off by default. Those modules are - UPDATE, GET, DELETE etc.

    There are two main reasons why this is used:

    1. To reduce module load time: If you click on the module to open it, all options for all RPCs need to be loaded before it opens. By using mode:edit, the module opens right away and RPC options are loaded when you open the select field. Imagine a module to create a donut:

      • RPC for the type of dough

      • RPC for the icing color or flavor

    Edit mode is also recommended in modules that will always be working with the lowest entity, for example, an attachment for an e-mail in a module.

    Mappable: false

    You may want to hide the mapping toggle to help the user enter correct information in a field.

    For example, in a select field type a user should select from a list of options. Having a map toggle is unnecessary and may confuse the user.

    To hide the mappable toggle, use the "mappable": false parameter.

    After switching on the mapping toggle, the text fields disappear.

    "mappable": false hides the mapping toggle:

    Messages (banners)

    In some circumstances, you may want to give users additional information in a module. 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.

    Information (blue)

    Warning (yellow)

    Danger (red)

    ID finder

    The ID finder allows users to identify items within a module by entering search criteria. The ID finder can either be the only search method for a field or it can be included in a list of multiple search methods.

    The ID finder window can either include a single search criterion (keyword or exact match) or multiple search criteria. There are specific guidelines to follow when implementing the ID finder, both within the module and the ID finder itself.

    Module guidelines for the ID finder

    When implementing the ID finder within a module, it is important to consider whether it is the only available search method to identify the item, or if there are multiple search methods. Regardless of the number of search methods, the ID finder button should always be labeled ID finder.

    If you are implementing the ID finder for a field that is not searching for an ID, for example the Dropbox > Watch files module with the File Path field, the button should read Finder instead of ID finder.

    Single search method

    If a module contains only the ID finder as a search method, the name of the field should be the [Item] ID that is being searched for.

    ID finder guidelines

    The ID finder can include either one single search criterion (keyword or exact match) or multiple search criteria. It is important to note that the number of results the ID finder will return is limited both on the Make platform side and the app side. Because of this, we do not advise users to leave the search field empty to return all results, as this information can be misleading.

    The standard blue info box message should be used for all ID finders, except for in cases of Single search criterion: exact match.

    Only a limited number of results can be shown. If you don’t see the item you’re looking for, try more specific search criteria.

    ID finder results

    If the API allows, the ID finder’s results should be listed in alphabetical order.

    ID finder template

    Support for custom query and filter 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.

    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.

    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.

    Search filters

    Provide a list of fields and a list of operators.

    Group operators by their data types

    Labels

    Labels are available for:

    • Array(s)

    • Array item(s)

      • Default to Item

      • This should be consistent and related to the label of the array. For example, if the label of an array is Recipients

    Universal module

    • If possible, provide a universal module.

    • The prefix path of the URL should not contain the version of the API, to ensure that users can use any version of the API. Even if there is no other version, it is good practice in case there is one in the future.

    • The hint under the URL should contain the correct prefix path of the URL, together with an example of a postfix path of the URL.

    • The example should be “ready-to-use”. We recommend using methods for retrieving a record in the example (GET).

    OAuth 2.0

    Connection is a link between Make and a third-party service or app. The OAuth 2.0 connection handles the token exchange automatically.

    Before you start configuring your OAuth 2.0 connection, create an app on a third-party service.

    When creating an app, use:

    • https://www.make.com/oauth/cb/app as a callback URL together with the oauth.makeRedirectUri variable, or:

    • https://www.make.com/oauth/cb/app as a callback URL together with the oauth.localRedirectUri

    Might require technical knowledge. A hint or link to documentation is necessary

  • Includes custom fields, if supported by the app

  • Hidden on the third-party’s Web UI

  • Placed at the bottom of all fields. This ensures that when users toggle to the advanced settings, they can easily view them without being mixed in with regular fields

  • Consider copying the UX from the third-party’s Web UI, for clarity

  • Limit should be the last variable

  • Ideally contain a default value

  • If an advanced field, must contain a default value

  • The field is only required to create but not update in an upsert module
  • A clear hint is mandatory to explain the condition

  • Should follow required fields and be located before optional fields

    • This could be an exception if the fields have to be arranged in logical blocks

  • to convert the type.

    Semi-dynamic fields

    It's also possible to use a combination of static and dynamic fields to customize the user experience, based on user settings and availability in the API.

    Even though this will be sent correctly, it is not user-friendly.

    The parameters birthday and due_date are correctly date typed and don't need to be formatted by the user who can use the now keyword.

    Communication:

    Parameter birthday is required to have format YYYY-MM-DD and parameter due_date is required to be a time stamp but nothing is done with the value.

    Parameters:

    The parameter due_date is an incorrect type and birthday is required to be formatted by the user.

    RPC for the type of sprinkles
  • RPC for filling options With this setup, the module will take a long time to load, especially if the API server is busy.

  • If we expect the user will want to map the value, not select from the list. For example, in a Watch for new orders > Mark order as received module, map the Order ID from the previous module.

  • [Item] ID field names include:

    • Campaign ID - campaign to be updated

    • Employee ID - employee record to delete

    • Etc.

    Multiple search methods

    If a module contains multiple search methods, they should be listed in an [Item] search method dropdown. Replace [Item] with the name of item that is being searched for.

    • [Item] search method field names include:

      • Spreadsheet search method - spreadsheet to add a row to

      • Channel search method - channel to send a message to

      • Record search method - record to update

      • Etc.

    • [Item] search method dropdown options include:

      • Search by keyword (ID finder with keyword search)

      • Search by [item] (ID finder with exact match search)

      • Select by file path

    Single search criteria

    Keyword search

    • The name of the search field should always be [Search item] Keywords.

      • [Search Item] keywords examples include:

        • Channel name keywords

        • Address keywords

    • Include the blue info box that instructs users to use more specific search criteria. In this case, that is a more specific keyword.

    Exact match required

    • The name of the search field should be identical to the name of the item that the user must match.

      • For example, if a user must enter an item’s name, the field should be Name.

    • Do not include the blue info box that tells users to use more specific search criteria, as there are no other criteria or way to make an exact match more specific.

    • In the search field hint, add the following: Must be the exact [Search item].

      • For example:

        • Field: Channel name.

        • Hint: Must be the exact Channel name.

    Multiple search criteria

    Keyword search

    • The name of the search field should always be [Search Item] keywords

      • [Search Item] keywords examples include:

        • Spreadsheet keywords

        • Employee Title keywords

    • Include the blue info box that instructs users to use more specific search criteria. In this case, that is either a more specific keyword or utilizing the other criteria in the ID finder to refine their search.

    Do not ask users to construct the query string.

    Do not share operator among all data types.

    , the label of an array item should be
    Recipient
    .
  • Button(s) to add an array item

    • Default to Add item.

    • The button label should be consistent and related to the label of array items. For example, if the label of an array item is Recipient , the label of the button should be Add recipient.

  • dynamic fields RPCs
    implement a custom IML function
    variable, 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 the oauth.redirectUri variable. This option is not suggested for new apps.

  • OAuth 2.0 authentication process

    OAuth 2.0 authentication processes consist of multiple steps that need to be defined in the Connection communication. The communication should be a collection with the keys below. You can use the keys the particular flow requires and disregard those that are unnecessary.

    Key
    Type
    Description

    preauthorize

    Request specification

    Describes a request that should be executed prior to the authorize directive.

    authorize

    Request specification

    Describes the 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 a user’s information. Most of the APIs have such a method. The info directive can be used to store account's metadata.

    Each section is responsible for executing its part in the OAuth 2.0 flow.

    You can describe the initial OAuth 2.0 flow as follows:

    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 to true. Otherwise, the token directive will not be successfully triggered.

    Components

    Communication

    For more information, see the communication documentation.

    • 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.

    This accessToken can be later accessed in any module that uses this connection or in the app base.

    response.expires

    The expires directive indicates the expiration datetime for tokens.

    It's generally used only in the token and refresh blocks of the connection communication and there are two variations:

    • response.data.expires is used to trigger the token refresh request.

    • response.expires is used to prompt the user to manually reauthorize the connection. This is necessary when the access token can no longer be automatically refreshed, such as when a refresh token expires. This is generally defined by the refresh_expires_in value in the response from the access token request, or specified in the documentation.

    When the date is reached, the connection needs to be reauthorized manually, either from a scenario or the Connections tab.

    If refresh_expires_in is not included in the reply but the API documentation mentions refresh tokens expire after a certain time, you should use that value. For example, for a year: "{{addYears(now, 1)}}"

    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 secrets.

    Available IML variables

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

    Variable
    Description

    now

    Current date and time

    environment

    TBD

    temp

    Contains custom variables created via temp directive.

    parameters

    Contains the connection’s input parameters.

    common

    Contains the connection’s common data collection.

    data

    Contains the connection’s data collection.

    Code grant example

    OAuth 2.0 code grant example with PCKE

    Client credentials example

    In the client credentials flow, the user must provide the Client ID and secret, so no common data is defined.

    {
        "url": "/api/users",
        "method": "POST",
        "body": {
            "name": "{{parameters.name}}",
            "birthday": "{{parameters.birthday}}",
            "due_date": "{{parameters.due_date}}"
        },
        "response": {
            "output": "{{body}}"
        }
    }
    [
        {
            "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"
        }
    ]
    {
        "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}}"
        }
    }
    [
        {
            "name": "name",
            "type": "text",
            "label": "Name"
        },
        {
            "name": "birthday",
            "type": "date",
            "label": "Birthday"
        },
        {
            "name": "due_date",
            "type": "date",
            "label": "Due Date"
        }
    ]
    [
    	{
    		"name": "array",
    		"type": "array",
    		"label": "PDF documents",
    		"required": true,
    		"spec": {
    			"type": "collection",
    			"label": "Document",
    			"spec": [
    				{
    					"name": "name",
    					"type": "filename",
    					"label": "Name",
    					"semantic": "file:name",
    					"required": true
    				},
    				{
    					"name": "data",
    					"type": "buffer",
    					"label": "Data",
    					"semantic": "file:data",
    					"required": true
    				}
    			]
    		},
    		"labels": {
    			"add": "Add document"
    		}
    	}
    ]
    {
        "type": "banner",
        "title": "This is an info",
        "text": "Here is a description of info.",
        "theme": "info"
    }
    {
    	"type": "banner",
    	"title": "This is a warning",
    	"text": "Here is a description of warning.",
    	"theme": "warning"
    }
    {
    	"type": "banner",
    	"title": "Headline, if needed...",
    	"text": "Minimum recommended length of message text. (50 characters)",
    	"theme": "danger"
    }
    {
        "name": "recordId",
        "type": "text",
        "label": "Record ID",
        "rpc": {
            "label": "ID finder",
            "url": "rpc://...",
            "parameters": [
                {
                    "type": "banner",
                    "text": "If you don't see the result you're looking for, try more specific search criteria.",
                    "theme": "info"
                },
                {
                    "name": "query",
                    "type": "text",
                    "label": "Query"
                }
            ]
        }
    }
    //If the API is expecting an array of collection
    // {
    //     "recipients": [
    //         {
    //              "name": "abc"
    //         }
    //     ]
    // }
    [
    	{
    		"name": "recipients",
    		"type": "array",
    		// Label of the array
    		"label": "Recipients",
    		"spec": {
    			"type": "collection",
    			// Label of array item(s)
    			"label": "Recipient",
    			"spec": [
    				{
    					"name": "name",
    					"type": "text",
    					"label": "Name",
    					"required": true
    				}
    			]
    		},
    		"labels": {
    			// Label of the button to add an array item
    			"add": "Add recipient"
    		}
    	}
    ]
    // If the API is expecting a primitive array
    // {
    //     "recipients": [
    //         "name-abc"
    //     ]
    // }
    [
    	{
    		"name": "recipients",
    		"type": "array",
    		// Label of the array
    		"label": "Recipients",
    		"spec": {
    			"type": "text",
    			// Label of array item(s)
    			"label": "Recipient name"
    		},
    		"labels": {
    			// Label of the button to add an array item
    			"add": "Add recipient name"
    		}
    	}
    ]
    {
        "response": {
            "data": {
                "accessToken": "{{body.token}}"
            }
        }
    }
    {
        "url": "http://example.com",
        "qs": {
            "token": "{{connection.accessToken}}"
        }
    }
    "token": {
        ...
        "response": {
            "data": {
              "expires": "{{addSeconds(now, body.expires_in)}}" // the access token expiration datetime
            }
            "expires": "{{addSeconds(now, body.refresh_expires_in)}}" // the refresh token expiration datetime
        }
    }
    {
        "clientId": "3j2h5g234iuy75467k3g457kj34bl",
        "clientSecret": "m034f8756y0cw3k8t7hkxl0esybv"
    }
    [ "user.read", "offline_access" ]
    [
        {
            "name": "clientId",
            "type": "text",
            "label": "Client ID",
            "advanced": true,
            "editable": true
        },
        {
            "name": "clientSecret",
            "type": "password",
            "label": "Client secret",
            "advanced": true,
            "editable": true
        },
        {
            "name": "scopes",
            "label": "Additional scopes",
            "type": "array",
            "labels": {
                "add": "Add scope"
            },
            "spec": {
                "type": "text",
                "label": "Scope"
            },
            "help": "Use this to get access to extra scopes.",
            "advanced": true,
            "editable": true
        }
    ]
    {
        "authorize": {
            "url": "https://example.com/authorize",
            "qs": {
                "scope": "{{join(distinct(merge(oauth.scope, ifempty(parameters.scopes, emptyarray))), ' ')}}",
                "client_id": "{{ifempty(parameters.clientId, common.clientId)}}",
                "redirect_uri": "{{oauth.localRedirectUri}}",
                "response_type": "code"
                // You generally don't need to specify the 'state' value, Make handles that for you.
            },
    
            "response": {
                "temp": {
                    "code": "{{query.code}}"
                }
            }
        },
        
        "token": {
            "url": "https://example.com/token",
            "method": "POST",
            "body": {
                "code": "{{temp.code}}",
                "client_id": "{{ifempty(parameters.clientId, common.clientId)}}",
                "grant_type": "authorization_code",
                "redirect_uri": "{{oauth.localRedirectUri}}",
                "client_secret": "{{ifempty(parameters.clientSecret, common.clientSecret)}}"
            },
            "type": "urlencoded",
    
            "response": {
                "data": {
                    "expires": "{{addSeconds(now, body.expires_in)}}", // access token expiration. This is used in the condition to trigger the "refresh" request.
                    "accessToken": "{{body.access_token}}",
                    "refreshToken": "{{body.refresh_token}}"
                },
                "expires": "{{addSeconds(now, body.refresh_expires_in)}}" // refresh token expiration. The connection needs manual reauthorization once this date is reached.
            },
            
            "log": {
                "sanitize": [
                    "request.body.code",
                    "request.body.client_secret",
                    "response.body.access_token",
                    "response.body.refresh_token"
                ]
            }
        },
        
        "refresh": {
            "condition": "{{data.expires < addMinutes(now, 15)}}",
            "url": "https://example.com/token",
            "method": "POST",
            "body": {
                "client_id": "{{ifempty(parameters.clientId, common.clientId)}}",
                "grant_type": "refresh_token",
                "client_secret": "{{ifempty(parameters.clientSecret, common.clientSecret)}}",
                "refresh_token": "{{data.refreshToken}}"
            },
            "type": "urlencoded",
            
            "response": {
                "data": {
                    "expires": "{{addSeconds(now, body.expires_in)}}", // access token expiration. This is used in the condition to trigger the "refresh" request.
                    "accessToken": "{{body.access_token}}",
                    "refreshToken": "{{body.refresh_token}}"
                }
            },
            
            "log": {
                "sanitize": [
                    "request.body.client_secret",
                    "request.body.refresh_token",
                    "response.body.access_token",
                    "response.body.refresh_token"
                ]
            }
        },
        
        "info": {
            "url": "https://example.com/me",
            "headers": {
                "authorization": "Bearer {{connection.accessToken}}"
            },
            
            "response": {
                "metadata": { // This is added under the connection name in the UI to make it easier for the user to identify it.
                    "type": "text",
                    "value": "{{body.data.name}} ({{body.data.email}})"
                },
                "uid": "{{body.data.id}}" // This is only needed if your app uses Shared webhooks. The ID allows Make to match the payloads del;ivered to the shared webhook to the correct recipients.
            },
            
            "log": {
                "sanitize": [ "request.headers.authorization" ]
            }
        }
    }
    {
        "clientId": "3j2h5g234iuy75467k3g457kj34bl",
        "clientSecret": "m034f8756y0cw3k8t7hkxl0esybv"
    }
    [ "user.read", "offline_access" ]
    [
        {
            "name": "clientId",
            "type": "text",
            "label": "Client ID",
            "advanced": true,
            "editable": true
        },
        {
            "name": "clientSecret",
            "type": "password",
            "label": "Client secret",
            "advanced": true,
            "editable": true
        },
        {
            "name": "scopes",
            "label": "Additional scopes",
            "type": "array",
            "labels": {
                "add": "Add scope"
            },
            "spec": {
                "type": "text",
                "label": "Scope"
            },
            "help": "Use this to get access to extra scopes.",
            "advanced": true,
            "editable": true
        }
    ]
    {
        "authorize": {
            "temp": {
                "code_verifier": "{{uuid}}"
            },
            "qs": {
                "scope": "{{join(distinct(merge(oauth.scope, ifempty(parameters.scopes, emptyarray))), ' ')}}",
                "client_id": "{{ifempty(parameters.clientId, common.clientId)}}",
                "code_challenge": "{{base64url(sha256(temp.code_verifier, 'base64'))}}",
                "code_challenge_method": "S256",
                "redirect_uri": "{{oauth.redirectUri}}",
                "response_type": "code"
            },
            "url": "https://example.com/authorize",
    
            "response": {
                "temp": {
                    "code": "{{query.code}}"
                }
            }
        },
    
        "token": {
            "url": "https://example.com/token",
            "body": {
                "code": "{{temp.code}}",
                "client_id": "{{ifempty(parameters.clientId, common.clientId)}}",
                "grant_type": "authorization_code",
                "redirect_uri": "{{oauth.redirectUri}}",
                "code_verifier": "{{temp.code_verifier}}"
            },
            "type": "urlencoded",
            "method": "POST",
    
            "response": {
                "data": {
                    "expires": "{{addSeconds(now, body.expires_in)}}", // access token expiration. This is used in the condition to trigger the "refresh" request.
                    "accessToken": "{{body.access_token}}",
                    "refreshToken": "{{body.refresh_token}}"
                },
                "expires": "{{addSeconds(now, body.refresh_expires_in)}}" // refresh token expiration. The connection needs manual reauthorization once this date is reached.
            },
    
            "log": {
                "sanitize": [
                    "request.body.code",
                    "response.body.access_token",
                    "response.body.refresh_token"
                ]
            }
        },
    
        "refresh": {
            "condition": "{{data.expires < addMinutes(now, 15)}}",
            "url": "https://example.com/token",
            "method": "POST",
            "body": {
                "grant_type": "refresh_token",
                "client_id": "{{ifempty(parameters.clientId, common.clientId)}}",
                "refresh_token": "{{data.refreshToken}}"
            },
            "type": "urlencoded",
    
            "response": {
                "data": {
                    "expires": "{{addSeconds(now, body.expires_in)}}", // access token expiration. This is used in the condition to trigger the "refresh" request.
                    "accessToken": "{{body.access_token}}",
                    "refreshToken": "{{body.refresh_token}}"
                }
            },
    
            "log": {
                "sanitize": [
                    "request.body.refresh_token",
                    "response.body.access_token",
                    "response.body.refresh_token"
                ]
            }
        },
        "info": {
            "url": "https://example.com/v1/info",
            "headers": {
                "authorization": "Bearer {{connection.accessToken}}"
            },
    
            "response": {
                "error": {
                    "message": "[{{statusCode}}] {{body.error}}"
                },
                "metadata": { // This is added under the connection name in the UI to make it easier for the user to identify it.
                    "type": "text",
                    "value": "{{body.user.fullName}} ({{body.user.email}})"
                },
                "data": {
                    "userId": "{{body.user.id}}"
                },
                "uid": "{{body.user.id}}" // This is only needed if your app uses Shared webhooks. The ID allows Make to match the payloads del;ivered to the shared webhook to the correct recipients.
            },
    
            "log": {
                "sanitize": [
                    "request.headers.authorization"
                ]
            }
        }
    }
    [ "user.read", "offline_access" ]
    [
        {
            "name": "clientId",
            "type": "text",
            "label": "Client ID",
            "required": true,
            "editable": true
        },
        {
            "name": "clientSecret",
            "type": "password",
            "label": "Client secret",
            "required": true,
            "editable": true
        },
        {
            "name": "scopes",
            "label": "Additional scopes",
            "type": "array",
            "labels": {
                "add": "Add scope"
            },
            "spec": {
                "type": "text",
                "label": "Scope"
            },
            "help": "Use this to get access to extra scopes.",
            "advanced": true,
            "editable": true
        }
    ]
    {
        "token": {
            "url": "https://example.com/token",
            "body": {
                "grant_type": "client_credentials",
                "client_id": "{{parameters.clientId}}",
                "client_secret": "{{parameters.clientSecret}}",
                "scope": "{{join(distinct(merge(oauth.scope, ifempty(parameters.scopes, emptyarray))), ' ')}}"
            },
            "type": "urlencoded",
            "method": "POST",
    
            "response": {
                "data": {
                    "expires": "{{addSeconds(now, body.expires_in)}}", // access token expiration. This is used in the condition to trigger the "refresh" request.
                    "accessToken": "{{body.access_token}}"
                }
            },
    
            "log": {
                "sanitize": [
                    "response.body.access_token"
                ]
            }
        },
        "refresh": {
            "condition": "{{data.expires < addMinutes(now, 15)}}"
    
            "url": "https://example.com/token",
            "body": {
                "grant_type": "client_credentials",
                "client_id": "{{connection.clientId}}",
                "client_secret": "{{connection.clientSecret}}",
                "scope": "{{join(distinct(merge(oauth.scope, ifempty(parameters.scopes, emptyarray))), ' ')}}"
            },
            "type": "urlencoded",
            "method": "POST",
    
            "response": {
                "data": {
                    "expires": "{{addSeconds(now, body.expires_in)}}", // access token expiration. This is used in the condition to trigger the "refresh" request.
                    "accessToken": "{{body.access_token}}"
                }
            },
    
            "log": {
                "sanitize": [
                    "response.body.access_token"
                ]
            }
        },
        "info": {
            "url": "https://example.com/v1/info",
            "headers": {
                "authorization": "Bearer {{connection.accessToken}}"
            },
    
            "response": {
                "error": {
                    "message": "[{{statusCode}}] {{body.error}}"
                },
                "metadata": { // This is added under the connection name in the UI to make it easier for the user to identify it.
                    "type": "text",
                    "value": "{{body.user.fullName}} ({{body.user.email}})"
                },
                "uid": "{{body.user.id}}" // This is only needed if your app uses Shared webhooks. The ID allows Make to match the payloads del;ivered to the shared webhook to the correct recipients.
            },
            
            "log": {
                "sanitize": [
                    "request.headers.authorization"
                ]
            }
        }
    }
    preauthorize => authorize => token => info

    refresh

    Request specification

    Describes a request that refreshes an access token.

    invalidate

    Request specification

    Describes a request that invalidates acquired access token.

    oauth.scope

    Contains an array of scope required to be passed to the OAuth 2.0 authorization process.

    oauth.redirectUri

    Contains the redirect URL for the OAuth 2.0 authorization process in this format: https://www.integromat.com/oauth/cb/app

    oauth.localRedirectUri

    Contains the redirect URL for the 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 the redirect URL for the OAuth 2.0 authorization process in this format: https://www.make.com/oauth/cb/app

    Select from list

  • Enter manually

  • Etc.