Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Make 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.
you want to use to build your custom app:
or
with vibe coding (information coming soon)
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 ).
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.
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.
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.
Coming soon.
In this section, you will learn the naming conventions used for modules in an app.
In this section, you will learn the naming conventions used for input parameters of an 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:
In this section, you will learn the best practices for developing custom apps.
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 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.
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"
If there are multiple types of connection, use parentheses for the connection type specification:
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 .
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:
To test your app in Make, create a new scenario.
In the Scenario Builder, add the Geocodify > Search geolocation module that you have just created.
Note that is has the Private tag.
Click Create a connection.
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.
The app theme is the module color in hexadecimal format. For example, the Make module's color is #6e21cc.
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:
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.
To install Make DevTool:
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
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.
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.
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.
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.
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.
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:
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.
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
In this section, you will learn best practices regarding the interface and how to process output parameters.
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.
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.
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.
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:
Go to the Parameters tab of the connection.
For each parameter with a value that should be kept secret, make sure that it is marked as type password.
In this section, you will learn best practices for the following regarding modules:
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.
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:
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.
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
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.
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.
When you are done with the updates and testing, review the Updating your apps article for the next steps.
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.
After you confirm that your app complies with the prerequisites, you can request an app review.
The review process consists of multiple stages. You can check the current status of the review in the Flow tab.
When Make approves your app, the app becomes available to all Make users. After approval, the process of your custom app management changes.
It is important to keep your app up to date and provide support to your app's users.
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.

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








{
"headers": {
"authorization": "Bearer {{connection.accessToken}}"
}
}{
"headers": "{{headerBuilderFunction()}}"
}{
"headers": {
"{{...}}": "{{headerBuilderFunction()}}"
}
}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
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.
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.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
}
]{
...
"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>"
]
}
...
}Enter your API Key from the Geocodify API.
Click Save.
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.
Click Save to save the module.
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.
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.

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

Click the Add to Chrome button. Confirm installation in the pop-up window.
Make DevTool is now installed in the Chrome Developer Tools pane.
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.
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.
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.
To clear the list of requests recorded by Make DevTool, click the bin icon next to the search field.
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.
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.
To open the module settings, double-click the module's name in the list.
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
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.
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"
}{
"baseUrl": "https://mydomain.freshsales.io"
}{
"baseUrl": "https://mailerlite.heroku.com/development"
}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 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.
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.
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 .
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.
A collection of resources to help you develop your custom app and learn more about using Make.
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.
To set up your custom app for Geocodify:
Log in to Make and go to the Custom Apps section.
In the upper-right corner, click + Create app.
In the pop-up window, fill in the app details. The chart below contains the values to use for your Geocodify app.
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.
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.
A private tag is displayed after your app's name on the app's page.
If your private app is not used in any of your scenarios, you can delete the private app.
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.
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.
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).
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.
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.
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 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.
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
},
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.
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.
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.
Debug custom IML functions with a variety of tools
To debug your IML functions, output the code and use a tool of your choosing.
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 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:
In this section, you will learn the naming conventions used for elements of an app.
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).
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".
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.
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, .
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
User ID
Organization, Company, Location, etc.
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.
{
"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"
}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.
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.
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.
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.
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.
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.
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.
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.
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.
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 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.
...
"log": {
"sanitize": ["request.headers.accesstoken"]
}
...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.
This is an example of how to handle two types of accounts - sandbox and production.
Add a checkbox in your connection parameters that can be checked when the condition is met:
Implement a condition in both the connection and the base:
All modules and remote procedures can then use hard-coded "url": "/uniqueEndpoint"
This is an example of how to handle two types of accounts - eu and us.
Set up select in your connection parameters, where you let your users choose from available environments:
Map the environment in both the connection and the base.
All modules and remote procedures can then use hard-coded "url": "/uniqueEndpoint"
Unlike a shared webhook, dedicated webhooks are directly linked to the user account. Only notifications for the specific user are received.
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.
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.
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.
When maintaining your app, you might need to apply changes or create a new app version.
The process is different between , and .
All changes take effect immediately in running scenarios with . You should take this into account while changing your code.
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.
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.
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 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.
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.
In this section, you will learn best practices regarding different types of input parameters and how to process them.
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.
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
If you need to roll back any changes you have made so far, contact us.
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.
A recommended tool for experimenting with regular expressions. Make sure to tick the ECMAScript (JavaScript) FLAVOR in the left panel.
A regular expressions generator for those who need help with regex.
A free editor to create and edit app logo files.
Contact us if you need help or you didn't find the information you need.
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.






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



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.


Delete a form






























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}}"
}
},
...{
"response": {
"error": {
"429": {
"type": "RateLimitError",
"message": "{{body.message}}"
},
"message": "{{body.message}}"
}
}
} 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.
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}}"
}
}
}
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;
}




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


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.
To set up the Base for your custom app:
Remove the default code that is present.
Copy and paste the following code:
"baseUrl": "https://api.geocodify.com/v2",
Contains the base URL of the API. Note that it contains the API version.
Click Save changes.
Continue to set up the module.
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.
In the Modules tab, click the switch to set the visible status of each module in the app that you want to publish.
You will see a new Review tab in the top panel.
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.
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
The review process contains three phases: form submission, automatic review, and manual review.
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.
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.
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.
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 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.

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.
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.
{
...
"info": {
"url": "https://example.com/api/me",
"headers": {
"authorization": "Bearer {{connection.accessToken}}"
},
"response"
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).
Use the Make web interface to debug Remote Procedure Calls (RPCs)
To find the RPC debug tool:
Navigate to the Custom apps area.
Select your custom app from the list.
Click the Remote Procedures tab.
Select the RPC you want to debug.
Click Test RPC.
Compare the tabs below to learn how to access information about your RPCs in Make.
After creating a new RPC, you can modify the default template in the Communications tab for your needs.
By default, the Parameters tab is empty. Here you can add any parameter you need. You can also add mappable parameters from a module. .
The RPC debug tool works the same way modules do.
Specify the connection and other fields (parameters) if needed.
Click the Test button.
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 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.
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:
Review the following sections to learn more about each prerequisite.
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.
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.
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.
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.
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.
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:
indicate something wrong on the client side.
indicate something wrong on the side of the third-party service.
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.
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:
Correct example of Search approach with validation:
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.
To generate the token, use the following code:
{
"url": "https://mock.api/connect",
"temp": {
"jwt": {
"iss": "https://iam.the.issu.er",
"iat": "{{timestamp}}",
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:
The payload to be signed.
The secret to sign the payload.
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.
There are six basic types of modules:
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.
Now that you have , we recommend completing these first steps before :
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.
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.
{
// 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}}",
...
}A custom header to customize the JWT authorization header. This parameter is optional.
Use to enable users to perform an arbitrary API call to the service. For example, Make an API call and Execute a GraphQL query.
Use to send processed data back to a webhook.











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 pagination if it's supported in the service API.


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







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.
{
"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}}"
}
}{
"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}}"
}
}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.
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:
Hints with links to documentation for more information.
A format code button with a label showing the data format. The available data formats are:
jsonc
json
javascript
markdown
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.
Hints displayed when hovering over the custom app configuration.
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.
A Save changes button to save changes in all editor boxes on the page.
Hovering over built-in IML functions shows detailed information.
Hovering over custom IML functions provides a link to the function definition.
Controls to expand and collapse the code. To view the controls, hover over the space between your code and editor line numbers.
The editor highlights the IML syntax so it's distinct from the rest of the code.
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:
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:
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:
To get your API token from Geocodify:
Got to the Geocodify website and click Sign Up to create an account.
Enter your details and click Register (or log in Google or GitHub).
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.
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.
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.
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.
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
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.
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.
In the Scenario Builder, add the HTTP > Make an API Key Auth request module.
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
Click Create.
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
Click Save.
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.
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.
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.
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.
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.
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.


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.
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.
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.
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.
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
User ID
Organization / Company / Location / Tenant
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.
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}}"
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.
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 page size should be as large as possible to reduce the number of requests, minimize delay, and avoid hitting the rate limit.
:
In this case, set limit to 100 in the request.
:
In this case, set page_size to 200.
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.
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:
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.
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):
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:
The Tools section of the Make DevTool has useful features that can help you build your scenario.
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.
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 for new data in a service and return it. They are trigger and instant trigger (webhook) modules.
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:
End result (mandatory): Clearly describes what the module achieves.
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.
{
"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",Searches module(s)' values for the specified term.
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.
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.
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.
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.
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.
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.
Duplicates a connection from the source module to every module of the same app in the scenario.
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.
Searches for specified variables in the scenario and replaces them with a new variable.
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.
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.
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.
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.
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.
Module
Select the module whose name you want to copy.
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.
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.
Highlights modules of the specified app in your scenario.
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.
Checks the size of modules in the scenario. This is useful when you are having trouble saving a blueprint that is too large.
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.
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.
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).
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





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.
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}}"
}
}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.
Used for modules that are updating an object. Most of the time these modules use a PATCH or PUT request.
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.







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




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.
{
"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}}"
}To create a new module for your Geocodify app:
In the Modules tab, click Create Module.
In the pop-up window, fill in the module details. The chart below contains the values to use for your Geocodify app.
Click Save.
Click the Mappable Parameters tab for your new search module.
Copy and paste the following code:
Click Save changes.
Click the Communications tab for your new search module.
Copy and paste the following code:
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.

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 write data into a service, modify data in a service, or retrieve a single result.
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 retrieve data from a service and allow for one or more results.
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 can perform an action on multiple records in a single call.
Bulk [action] [parameter] (advanced)
Bulk upload call conversions (advanced) Bulk create folders (advanced)
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 ( ).
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)
Watch modules watch for new data in a service and return it. They are trigger and instant trigger (webhook) modules.
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 write data into a service, modify data in a service, or retrieve a single result.
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 retrieve data from a service and allow for one or more results.
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 are modules used to make an API call or query when a pre-built module does not exist.
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
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:
to get any relevant information from the user that adds the connection (credentials, API key, etc.)
to process the authentication
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:
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}}.
To set up the connections for your custom app:
In the Connections component, click Create Connection.
Fill in the details of your connection.
Label: Geocodify API key
Continue to set up the .
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.
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.
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.
Part of the response from Pipedrive API /activities :
from Pipedrive
Part of the response from Virtuagym API /api/v0/activity:
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.
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.
When the service returns an HTTP error, it is not possible to evaluate it as a success.
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.
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.
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.
When handling an error, you can specify the type of the error.
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)
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
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.
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.
The response.uid directive allows you to save the user’s remote service ID. This is required when using .
the user needs to provide when setting up a new connection.
like secrets.
These IML variables are available for you to use everywhere in this module:
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.
Instead, we suggest using a separate module for each API call:
to avoid receiving misleading or incorrect error messages and,
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.






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.



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.

/// 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
Click Save.
Select the Parameters tab and remove the default parameter that is present.
Copy and paste the following code:
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
Click Save changes.
In the Communication tab, remove the code present.
Copy and paste the following code:
"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.
Click Save changes.

to allow users to set up error handling for individual modules.
The following are exceptions when you may want to use a batch action:
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
Get record by ID
PUT the record patched by user’s input
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).
Some services only return an ID after a record update.
Chain of actions: Write > Read
Update the record
Get the record by ID
Workday Financial Management > Update a Supplier Invoice
Chain of actions: Write > Write > … > Write
Create an upload session
Keep sending chunks
Finalize the upload
Dropbox > Upload a File
Chain of actions: Read > Read
Get the media ID
Download file by media ID
WhatsApp Business Cloud > Download a Media
Telegram Bot > Download a File
Requests involving processes running asynchronously on 3rd party service.
Chain of actions: Write > Read > Read > … > Read
Start the task
Keep polling for task status
Get the result when done
Microsoft Power Automate: Trigger a Desktop Flow
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.
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.
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:
Create a call
Check the status of the call
Request the result of the call
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.
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.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.
Required: no
This directive lets you customize Make's response on the webhook or a verification request.
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
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.
condition
IML string
Specifies when to treat the incoming data as a verification request.
respond
IML string
Specifies the response.
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.
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.
Properties of the iterate directive are described in the communication documentation.
Properties of the output directive are described in the communication documentation.
Properties of the condition directive are described in the communication documentation.
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.
These IML variables are available for you to use everywhere in a webhook:
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.
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 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.
You can use static parameters inside the action module without any restrictions.
You can use mappable parameters inside the action module without any restrictions.
The action module should always output only one bundle.
To help the users with setting up your module, you can provide samples.
When using an OAuth type of connection, use the scope to define scopes required by this action.
These IML variables are available for you to use everywhere in this module:
now
Current date and time
environment
TBD
temp
Contains custom variables created via 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:
output
When using the wrapper directive, the output variable represents the result of the outputdirective.
Additional variables available after using the iterate directive, i.e. in wrapper or pagination directives:
iterate.container.first
Represents the first item of the array you iterated.
iterate.container.last
Represents the last item of the array you iterated.
Additional variables available for pagination and response objects:
body
Contains the body that was retrieved from the last request.
headers
Contains the response headers that were retrieved from the last request.
items
When iterating this variable represents the current item that is being iterated.
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
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.
Expected input
Result
Example
Additional information
The name of the field should always match what the user sees in the 3rd party UI.
The process to obtain these values should be documented in our Help Center if the fields are required.
The process to obtain these values do not need to be documented in our Help Center as the fields are not required.
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.
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.
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.
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:
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.
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.
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.
If you need to make sure that the module is not used anymore, you can throw an error whenever the module is executed.
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:
Remove the current primary connection.
Map the new connection as the primary connection.
Map the deprecated connection as the alternative connection.
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.
[
{
"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.

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.

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."
}
}
}Link
What if left empty
Describe the impact of not entering a value.
Format
If left empty, default formatting is used.
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.
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.
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.
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.
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}}",
// ...
}[
{
"name": "email",
"type": "email",
"label": "Email address",
"required": true
},
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.
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.
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 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.
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.
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.
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.
{
...
"body": {
"firstName": "{{parameters.firstName}}",
"lastName": "{{parameters.lastName}}",
"email": "{{parameters.email}}"
},
// ... other directives
}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
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.
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.
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.
Parameters the user needs to provide when setting up a new connection.
Default scope for every new connection.
Collection of available scopes.
Non-user-specific sensitive values like secrets.
The OAuth 1.0 authentication process consists of multiple steps. You can fill in the necessary sections and delete those that are unnecessary.
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.
These IML variables are available for you to use everywhere in this module:
now
Current date and time
environment
TBD
temp
Contains custom variables created via temp directive.
parameters
Contains the connection’s input parameters.
common
Contains the connection’s common data collection.
data
Contains the connection's data collection.
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.
For additional information, see our documentation.
If API supports pagination, you can implement it by using the .
You can use inside the search module without any restrictions.
You can use inside the search module without any restrictions.
Unlike the action module, the search module can .
To help the users with setting up your module, provide .
When using an OAuth type of connection, use the to define scopes required by this module.
These IML variables are available for you to use everywhere in this module:
Additional variables available for the response object:
Additional variables available after using the iterate directive, i.e. in wrapper or pagination directives:
Additional variables available for pagination and response objects:
{
"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.
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.

[
{
"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"
}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.
The communication response is extended with the trigger object.
The trigger collection specifies directives that control how the trigger works and how your data is processed.
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
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.
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.
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}}:
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:
The Epoch panel is a specific component of the trigger allowing a user to choose the starting item.
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.
The trigger module can return multiple bundles at once.
To help the users with setting up your module, provide samples.
When using an OAuth type of connection, use the scope to define scopes required by this trigger.
These IML variables are available for you to use everywhere in this module:
now
Current date and time
environment
TBD
temp
Contains custom variables created via 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:
output
When using the wrapper directive, the output variable represents the result of the outputdirective.
Additional variables available after using the iterate directive, i.e. in wrapper or pagination directives:
iterate.container.first
Represents the first item of the array you iterated.
iterate.container.last
Represents the last item of the array you iterated.
Additional variables available for pagination and response objects:
body
Contains the body that was retrieved from the last request.
headers
Contains the response headers that were retrieved from the last request.
items
When iterating this variable represents the current item that is being iterated.
{
"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.

A mappable parameter is a variable or setting that you can change or link (map) to another value.
You can use mappable parameters in many types of fields:
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.
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.
Communication:
Parameters:
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.
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.
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:
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.
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.
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.
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.
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.
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.
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.
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
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).
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
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
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.
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:
Parameters:
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
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.
RecipientButton(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.


















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 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.
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.
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
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.
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.
Parameters the user needs to provide when setting up a new connection.
Default scope for every new connection.
Collection of available scopes.
Non-user-specific sensitive values like secrets.
These IML variables are available for you to use everywhere in this module:
now
Current date and time
environment
TBD
temp
Contains custom variables created via temp directive.
parameters
Contains the connection’s input parameters.
common
Contains the connection’s common data collection.
data
Contains the connection’s data collection.
{
"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 => inforefresh
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.

