Do not index
Do not index
In this guide, we will create a Productive app for Make. If you're a SaaS leader intrigued by expanding your software's accessibility to over 1500 third-party apps within the Make ecosystem, this guide provides a practical demonstration of the process. By the end, you'll know how to connect your software to Make.
Here’s what we’ll cover:
What is a Make app?
Make is a no-code platform with over 500,000 users across 198 countries that connects apps and moves data between them. Make apps are integrations that use third-party APIs and include modules. Modules are steps in a Make user’s automation. They can be actions, searches, triggers, aggregators, or iterators. By building an app for Make, users can incorporate your app into their automations on the Make platform.
Why build a Productive app for Make?
Productive serves marketing, creative, and software development agencies as an all-in-one management system. There isn’t currently a Make app for Productive making it a suitable candidate for our guide, especially since there are active requests for this integration on Make’s wishlist.
By integrating Productive with Make, users can automate tasks that currently require manual input. For example:
- Automate Task Creation: Create Productive tasks from new Google Calendar events automatically
- Project Kickoff Automation: Start new projects in Productive instantly when contracts are signed in PandaDoc
- Streamlined HR Processes: Schedule employee time off directly from Slack messages, simplifying HR tasks and enhancing response times.
What we’ll build
This guide walks you through creating a basic version of a Productive app for Make.
The app we create will:
- Authenticate with your Productive account.
- Select from your account’s managers, workflows, and companies to assign to the project.
- Create a new project from within Make.
Before you start, make sure you have:
How to build a Productive app for Make: 7 steps
1. Create a new app
Log into Make, navigate to "My Apps," and select “create a new app”
Next, complete the form with your apps details.
Once done, we'll proceed to let users input their API keys and establish a connection.
2. Build the connection
Go to the "Connections" tab. Click "Create a New Connection" and choose "API Key" as the type. Label it "Productive".
Productive needs both the organization ID and the API token to authorize the connection.
In the “Parameters” tab, enter the organization ID and Auth Token. Users will provide both to connect Productive in Make.
[
{
"name": "organizationId",
"type": "text",
"label": "Organization Id",
"required": true
},
{
"name": "apiToken",
"type": "text",
"label": "Auth Token",
"required": true
}
]
Next, visit the “Communication” tab to set up account validation. Use Productive’s users endpoint to verify the API key. This confirms the API key's functionality and adds authorized user details to the connection label.
We also sanitize the auth token to remove sensitive information, protecting it from exposure in logs.
Paste the following code into the "Communication" tab to finalize the connection:
{
// Request
"url": "https://api.productive.io/api/v2/users", // Absolute URL to the API endpoint which validates credentials
"headers": { // Additional HTTP headers
"Content-Type": "application/vnd.api+json",
"X-Auth-Token": "{{parameters.apiToken}}",
"X-Organization-Id": "{{parameters.organizationId}}"
},
// Response handling
"response": {
"metadata": { // Adds authorized user details to the connection label.
"type": "email", // Type of the parameter. Can be "text" or "email".
"value": "{{first(map(body.data, 'attributes.email'))}}"
},
"error": { // Error handling
"message": "[{{statusCode}}] {{body.error}}" // On error, returns error message as "[statusCode] error text".
}
},
"log": {
"sanitize": [ // Excludes sensitive parameters from logs.
"request.headers.X-Auth-Token"
]
}
}
Now, your connection tab is all set. Next, let's configure the base.
3. Configure the Base
The base holds all components that are common to all modules and remote procedures. Setting things up here ensures they are inherited by all modules and remote procedures, helping us avoid repetition.
We'll set up several key items:
- BASE URL: This is the prefix for every API request made by our modules and remote procedures.
- Headers: These are the default headers every module will use to authorize API requests, so we don't need to set them within each module.
- Response: We define default directives for handling responses, like error management.
- Logs: Default directives for handling logs include sanitizing sensitive data.
Here is the code to input into this section:
{
"baseUrl": "https://api.productive.io/api/v2",
"headers": {
"Content-Type": "application/vnd.api+json",
"X-Auth-Token": "{{connection.apiToken}}",
"X-Organization-Id": "{{connection.organizationId}}"
},
// Updated response handling to extract status and detail from the first error
"response": {
"error": {
"message": "{{join(map(body.errors; 'detail'); '; ')}}"
}
}
,
"log": {
"sanitize": [
"request.headers.X-Auth-Token"
]
}
}
Don’t forget to save your changes. Next, we’ll move on to our remote procedure calls, which will allow users to choose from existing projects, companies, and workflows in their Productive account.
4. Remote procedure calls (RPC)
Remote Procedure Calls (RPC) are functions that fetch additional data within a module. They are useful for fields that require dynamic data like IDs, which are hard for users to guess. This makes the user interface friendlier than requiring users to input static IDs.
We use RPCs for companies, people, and workflows to dynamically fetch these details for users when they start a new project. We prefetch these items to simplify user selection in the dropdown menu.
Create the RPC for companies
First, create the RPC for companies. Go to the "Remote Procedure Calls" tab, click "Create New Remote Procedure," and fill in the name and label as "rpcCompany" and select "Productive" for the connection.
Next, in the "Communication" tab, retrieve the list of companies from Productive's companies endpoint. We use pagination and limits to ensure the app loads quickly.
Paste the following code into the remote procedure section. This RPC does not accept parameters.
{
"url": "/companies",
"method": "GET",
"qs": { },
"body": { },
"headers": { },
"response": {
"limit": 15,
"iterate": "{{body.data}}",
"output": {
"label": "{{item.attributes.name}}",
"value": "{{item.id}}"
}
},
"pagination": {
"qs": {
"page": "{{pagination.page}}"
},
"condition": "{{body.TotalPages >= pagination.page}}"
}
}
Create the RPC for People
Next, let's set up a remote procedure call to fetch all people within the organization. This allows users to select someone from their organization to assign as a project manager. Go to the "Remote Procedure Calls" tab, click "Create New Remote Procedure," and fill in the name and label as "rpcPeople." Choose "Productive" for the connection.
In the "Communication" tab, retrieve the list of people from Productive's people endpoint. We also use pagination and limits to ensure the app loads quickly. This RPC does not require parameters, so leave that section blank.
Paste the following code into the "Communication" tab to complete this setup:
{
"url": "/people",
"method": "GET",
"qs": { },
"body": { },
"headers": { },
"response": {
"limit": 15,
"iterate": "{{body.data}}",
"output": {
"label": "{{item.attributes.first_name}} {{item.attributes.last_name}}",
"value": "{{item.id}}"
}
},
"pagination": {
"qs": {
"page": "{{pagination.page}}"
},
"condition": "{{body.TotalPages >= pagination.page}}"
}
}
Create the RPC for workflows
For our last remote procedure call, we'll set up one for workflows. In Productive, new projects need to be linked to specific workflows, so we'll dynamically fetch this information for the user.
Go to the "Remote Procedure Calls" tab, click "Create New Remote Procedure," and enter "rpcWorkflows" for both the name and label. Choose "Productive" for the connection.
In the "COMMUNICATIONS" tab, access the list of workflows from Productive's workflow endpoint. We use pagination and limits to ensure the app loads efficiently. This RPC does not require any parameters.
Paste the following code into the "Communication" tab:
{
"url": "/workflows",
"method": "GET",
"qs": { },
"body": { },
"headers": { },
"response": {
"limit": 15,
"iterate": "{{body.data}}",
"output": {
"label": "{{item.attributes.name}}",
"value": "{{item.id}}"
}
},
"pagination": {
"qs": {
"page": "{{pagination.page}}"
},
"condition": "{{body.TotalPages >= pagination.page}}"
}
}
With this final step, we are now ready to create a module!
5. Create the action module
Go to the "MODULES" tab and create a new module.
Add mappable parameters, which allow variables from other modules to be included. This feature is particularly useful for creating multi-step apps. We will use the remote procedure calls we set up earlier.
In the "MAPPABLE PARAMATERS" tab, replace the existing parameters with the list below. Note that we are using a select type for Company ID, Project Manager, and Workflow ID:
[
{
"name": "name",
"type": "text",
"label": "Project Name",
"required": true
},
{
"name": "company_id",
"type": "select",
"label": "Company ID",
"options": "rpc://rpcCompany",
"required": true
},
{
"name": "project_manager_id",
"type": "select",
"label": "Project Manager",
"options": "rpc://rpcPeople",
"required": true
},
{
"name": "workflow_id",
"type": "select",
"label": "Workflow ID",
"options": "rpc://rpcWorkflows",
"required": true
},
{
"name": "public_access",
"type": "boolean",
"label": "Access",
"required": true
}
]
Next, configure our code in the "COMMUNICATION" tab. We’ll make a request to Productive’s projects endpoint to create a new project.
Paste the following code into the "COMMUNICATION" tab:
{
// Request to API endpoint.
"url": "/projects", // Relative to base URL
"method": "POST",
"headers": { }, // Additional HTTP headers
"qs": { }, // Query string
"body": {
"data": {
"type": "projects",
"attributes": {
"name": "{{parameters.name}}",
"project_type_id": 2,
"public_access": "{{parameters.public_access}}"
},
"relationships": {
"company": {
"data": {
"type": "companies",
"id": "{{parameters.company_id}}"
}
},
"project_manager": {
"data": {
"type": "people",
"id": "{{parameters.project_manager_id}}"
}
},
"workflow": {
"data": {
"type": "workflows",
"id": "{{parameters.workflow_id}}"
}
}
}
}
}
,
// Response handling
"response": {
"output": "{{body}}" // Returns API response body as an output bundle.
}
}
Next, we'll generate our interface. The interface outlines the output structure and specifies the parameters used in the following modules. Make offers a tool for generating interfaces. You can find a sample response in Productive's documentation and paste it directly into Make’s interface generation tool.
Alternatively, you can paste the provided format into the interface tab.
Code to paste into the interface
// Defines JSON object with "id" parameter as expected API response body.
[
{
"name": "data",
"type": "collection",
"spec": [
{
"name": "id",
"type": "text"
},
{
"name": "type",
"type": "text"
},
{
"name": "attributes",
"type": "collection",
"spec": [
{
"name": "name",
"type": "text"
},
{
"name": "number",
"type": "text"
},
{
"name": "project_number",
"type": "text"
},
{
"name": "project_type_id",
"type": "number"
},
{
"name": "project_color_id",
"type": "text"
},
{
"name": "last_activity_at",
"type": "text"
},
{
"name": "public_access",
"type": "boolean"
},
{
"name": "time_on_tasks",
"type": "boolean"
},
{
"name": "tag_colors",
"type": "collection",
"spec": [ ]
},
{
"name": "archived_at",
"type": "text"
},
{
"name": "created_at",
"type": "text"
},
{
"name": "template",
"type": "boolean"
},
{
"name": "custom_fields",
"type": "text"
},
{
"name": "task_custom_fields_ids",
"type": "text"
},
{
"name": "sample_data",
"type": "boolean"
}
]
},
{
"name": "relationships",
"type": "collection",
"spec": [
{
"name": "organization",
"type": "collection",
"spec": [
{
"name": "data",
"type": "collection",
"spec": [
{
"name": "type",
"type": "text"
},
{
"name": "id",
"type": "text"
}
]
}
]
},
{
"name": "company",
"type": "collection",
"spec": [
{
"name": "meta",
"type": "collection",
"spec": [
{
"name": "included",
"type": "boolean"
}
]
}
]
},
{
"name": "project_manager",
"type": "collection",
"spec": [
{
"name": "meta",
"type": "collection",
"spec": [
{
"name": "included",
"type": "boolean"
}
]
}
]
},
{
"name": "last_actor",
"type": "collection",
"spec": [
{
"name": "meta",
"type": "collection",
"spec": [
{
"name": "included",
"type": "boolean"
}
]
}
]
},
{
"name": "workflow",
"type": "collection",
"spec": [
{
"name": "meta",
"type": "collection",
"spec": [
{
"name": "included",
"type": "boolean"
}
]
}
]
},
{
"name": "custom_field_people",
"type": "collection",
"spec": [
{
"name": "meta",
"type": "collection",
"spec": [
{
"name": "included",
"type": "boolean"
}
]
}
]
},
{
"name": "template_object",
"type": "collection",
"spec": [
{
"name": "meta",
"type": "collection",
"spec": [
{
"name": "included",
"type": "boolean"
}
]
}
]
}
]
}
],
"label": "Data"
},
{
"name": "meta",
"type": "collection",
"spec": [ ],
"label": "Meta"
}
]
Our app is now complete! Let's give it a try!
6. Try it out
Let's put our app to the test! Start by navigating to the scenarios section.
Create a new scenario by clicking “Create a new scenario”
In the apps section of the scenario, choose "productive" and then select the "Create new project" module.
Now, create a new connection. Retrieve your organization ID from the settings tab in your Productive account under API integrations.
Generate a token, ensuring it includes read/write access.
Copy both the token and organization ID.
Paste the tokens into the connection form in your Make scenario, along with your organization ID, and save.
Fill out the form, click "OK", and run the scenario to ensure it functions properly.
Confirm by clicking "Yes" in the popup and selecting "Run Once".
If all goes well, you should find a new project in your Productive account under Project Management, then Projects.
Congratulations! You've successfully tested your Productive app!
7. Publishing
By default, our app is private and only accessible to us. If we want to share it with others, there are two options for making it public.
- Published (Approval Not Requested): Share the app with users from any organization using the invitation link. Users who receive the share link must have administration rights or app install permissions granted to install and use the app in the scenario builder.
- Published (Pending Approval): If you’d like the app to be available globally to all Make users, you’ll need to get Make’s approval. Once the app is submitted for approval, this status is assigned. It indicates that the app is under review and awaiting approval from Make’s review team.
For this tutorial, we can keep this app private, but it’s an important consideration as you build Make apps whether you want them to be available more globally.
It's important to note that once the app is published, it cannot be made private again.
Conclusion
That's a wrap! While this example included one module, there are many ways to expand it.
- Add more action modules and endpoints from Productive documentation, based on community requests.
- Create trigger modules so users can create scenarios based on changes in their Productive accounts.
- Update branding elements and add a custom logo.
Are you working on a Make app? If so, I'd love to hear about it!