
Make Scenario Building
- 225 installs
- 75 repo stars
- Updated July 21, 2026
- integromat/make-skills
Design a Make scenario by clarifying the automation need, choosing modules, and composing flows with routing, branching, and error handling.
About
Guides designing a Make scenario: clarifying the business use case, choosing which modules to use, and composing them into flows with routing, branching, and error handling. A developer uses it to architect an automated Make workflow before configuring individual modules.
- Guides choosing which modules to use and composing routing, branching, filtering, and aggregations
- Enforces business-need clarification and provider disambiguation before building a blueprint
Make Scenario Building by the numbers
- 225 all-time installs (skills.sh)
- +13 installs in the week ending Aug 2, 2026 (Skillselion tracking)
- Ranked #563 of 2,715 Automation & Workflows skills by installs in the Skillselion catalog
- Data as of Aug 4, 2026 (Skillselion catalog sync)
npx skills add https://github.com/integromat/make-skills --skill make-scenario-buildingAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 225 |
|---|---|
| repo stars | ★ 75 |
| Last updated | July 21, 2026 |
| Repository | integromat/make-skills ↗ |
What it does
Design a Make scenario by clarifying the automation need, choosing modules, and composing flows with routing, branching, and error handling.
Files
Make Scenario Building
This skill guides building a scenario in Make. A scenario is an automated workflow composed of modules connected together. Before building anything, Phase 1 below MUST be completed.
Known Make module id: the Make Code module is "module": "code:ExecuteCode".
Phase 1: Understand the Business Need & Identify Modules
Phase 1 has three steps. Do not skip or rush any of them.
Step 1: Clarify the Business Use Case
The first job is to understand exactly what the user wants to automate. Use an adaptive interview approach:
1. Start conversational. Ask 1-2 focused questions about what they want to achieve:
- What task or process do they want to automate?
- Which systems or services are involved?
2. Drill deeper based on answers. Once the basics are clear, clarify:
- What triggers the automation? (time interval, webhook, manual execution)
- What data moves between systems and in which direction?
- Are there any conditions, branching logic, or error handling needs?
3. If answers are vague, get structured. Ask explicitly:
- "What is the source system and what data are you pulling from it?"
- "What is the destination system and what should happen there?"
- "Should this run on a schedule, on-demand, or when an event occurs?"
CRITICAL — Provider disambiguation (MUST follow): When the user mentions a generic category OR describes a capability without naming a specific app, the agent MUST ask which provider/service they use BEFORE proceeding to Step 2. Never assume a provider.
Common ambiguous categories and their possible providers (non-exhaustive — apply the same logic to any category not listed):
- Forms/surveys: Google Forms, Typeform, JotForm, Tally, Microsoft Forms, SurveyMonkey, ...
- AI/LLM: OpenAI, Anthropic, Google AI, Make AI, Azure OpenAI, Cohere, ...
- Email: Gmail, Outlook, SendGrid, Mailchimp, SMTP, ...
- Calendar: Google Calendar, Outlook Calendar, Calendly, ...
- Cloud storage: Google Drive, Dropbox, OneDrive, Box, ...
- CRM: Salesforce, HubSpot, Pipedrive, Zoho CRM, ...
- Databases: Airtable, Google Sheets, PostgreSQL, MySQL, MongoDB, ...
- Project management: Jira, Asana, Monday.com, Trello, ClickUp, Linear, ...
- Messaging/chat: Slack, Discord, Microsoft Teams, Telegram, ...
This also applies when the user describes a capability rather than naming an app. Words like "summarize", "analyze sentiment", "feedback form", "send a notification", or "store data" describe what they want to do — not which service to use. Ask.
Bad: User says "list responses from my feedback form and post a sentiment summary into Discord." Agent assumes Google Forms and OpenAI and proceeds.
Good: Same request. Agent asks: "Which form tool holds your responses — Google Forms, Typeform, JotForm, or something else? And for sentiment analysis, do you want to use OpenAI, Anthropic, Make AI, or another AI service?"
Different providers have different modules, capabilities, and connection requirements — guessing wastes time and produces wrong blueprints.
Continue until the use case can be clearly articulated in one paragraph. Every app in the scenario must be explicitly identified by name — if any app is still a generic category or implied by a capability description, ask before proceeding. Do NOT proceed to Step 2 until the business need is fully understood.
Step 2: Identify Make Modules
Once the use case is clear, map it to Make modules using the MCP tools available from the Make MCP server:
1. Find relevant apps. Use the apps_recommend tool (or a similarly named tool if unavailable) with a description of the user's need. This returns recommended Make apps for the involved systems.
- One app per call. Never batch multiple apps in a single
apps_recommendcall. Call separately for each distinct app/service. These calls can run in parallel.
2. List available modules. For each relevant app, use the app_modules_list tool (or similarly named) to see what modules (triggers, actions, searches, transformers) are available. Pass the appVersion returned by apps_recommend.
3. Get app documentation. For each app, call app_documentation_get using the exact appName value returned by apps_recommend (do not abbreviate or modify it). This returns detailed capabilities and module descriptions. Call once per app, not per module.
4. Select modules. Pick the specific modules needed:
- Trigger module — what starts the scenario (e.g., Watch New Rows, Webhook, Schedule)
- Action modules — what the scenario does (e.g., Create Record, Send Message, Update Row)
- Utility modules — if needed for data transformation, iteration, aggregation, routing, or error handling
If a tool is not found by exact name, search for similarly named tools on the Make MCP server. The key capability needed is: recommending apps and listing their modules.
Module name verification: Never guess module names. Always verify via app_modules_list. Case and spelling must match exactly.
No scheduler module: There is no scheduler module in Make. Scheduling is a scenario-level setting, not a module. The scenario always starts with the first module (trigger or action). Scheduling is configured separately via the scenario_scheduling_update tool.
IMPORTANT: As modules are identified, record the following details from the tool responses — they are needed in subsequent phases:
- App name (exact name as returned by the tool)
- App version (exact version as returned by the tool)
- Module name (exact technical name/slug of each module you plan to use)
Step 2.5: Look Up Reference Templates
Once apps and modules are identified, search the Make public template library for similar scenarios. Studying an existing template's blueprint reveals canonical module versions, mapper shapes, and aggregator/feeder bindings that aren't visible from app-module_get alone.
Recommended whenever the planned flow includes:
- An aggregator (
util:TextAggregator,builtin:BasicAggregator, etc.) - A Make AI Tools module or any AI provider module
- An app you haven't configured earlier in this session
- Multi-module composition (more than 2-3 modules, branching, iteration)
Skip allowed for single-module trigger → single-action flows with well-known apps and no aggregation/AI.
How: 1. public-templates_list with name: <use-case keywords> and usedApps: <app slugs from Step 2> 2. If matches found, pick the highest-usage one with the most app overlap 3. public-templates_get-blueprint to fetch the full structure 4. Use as STRUCTURAL reference — NOT as the literal blueprint to copy. Templates often implement a slightly different pattern (e.g., per-item loop vs digest-style aggregation). Note diffs and present them to the user in Step 3.
If public-templates_get* returns "Organization-bound request can't be used outside of the Organization Context", retry once; if it persists, reconnect the Make MCP server (/mcp reauth) and retry. If still failing, proceed without — the template is a nice-to-have reference, not a requirement.
See Templates Lookup for search patterns, blueprint-diffing tips, and MCP workarounds. The top 10 most-used public templates are also kept locally under examples/popular-templates/ — check there first when the user's request is a near-match for a common automation (e.g., AI enrichment of Sheets rows, webhook → Sheets, chatbot reply) and skip the remote round-trip.
Step 3: Present the Module Composition & Get Confirmation
Present the proposed module sequence to the user using flowchart notation:
Linear flow:
Trigger: Google Sheets - Watch New Rows → Slack - Send Message → Google Drive - Upload FileBranching flow (with If-Else + Merge) — mutually exclusive branches that converge:
Trigger: Webhook → HTTP - Make a Request → If-Else
├─ If (status = "success"): Slack - Send Message
└─ Else: Email - Send Error
→ Merge → Google Sheets - Log ResultBranching flow (with Router) — multiple branches can fire, no convergence:
Trigger: Webhook → HTTP - Make a Request → Router
├─ Route A (status = success): Slack - Send Message
└─ Route B (priority = high): Email - Send AlertFlow with iteration:
Trigger: Schedule → Google Sheets - Search Rows → Iterator → Slack - Send Message (for each row)For each module in the sequence, briefly note:
- The app and module name
- What it does in this scenario (1 sentence)
Then ask the user: "Does this module composition achieve what you need? Should I adjust any steps?"
Do NOT proceed beyond Phase 1 until the user confirms the composition is correct. The literal scenario layout and module configuration will be handled in subsequent phases.
Phase 1 Output
Once the user confirms, produce a Scenario Plan summary to carry into subsequent phases. This is the working reference — do not lose it.
## Scenario Plan
**Use case:** <one paragraph summary>
**Trigger type:** <schedule | webhook | manual | event-based>
### Modules
| # | App | App Version | Module (slug) | Module Label | Role in scenario |
|---|-----|-------------|---------------|--------------|------------------|
| 1 | ... | ... | ... | ... | ... |
| 2 | ... | ... | ... | ... | ... |
### Flow
<flowchart notation from Step 3>This table is the source of truth for which apps and modules will be used. Subsequent phases will reference it directly.
CRITICAL — Plan adherence: Once the user confirms the Scenario Plan, treat it as a binding contract. If during Phase 2 a reason emerges to change the module composition, flow structure, or branching pattern (e.g., switching from If-Else + Merge to Router, adding or removing modules, changing the trigger type), STOP and present the proposed change to the user with a clear explanation of why, along with an updated plan summary highlighting what has changed. Do NOT silently deviate from the confirmed plan.
---
Phase 2: Configure, Deploy & Verify
Once the user confirms the module composition from Phase 1, proceed through these steps:
Step 1: Secure Connections (REQUIRED — never auto-select)
CRITICAL — STOP and ask before proceeding. This is an interactive checkpoint. For EVERY app that needs a connection — even if only one matching connection exists — list the options and ask the user which connection to use. Do NOT auto-select a connection. Do NOT call any module-specific RPCs (rpc_execute for spreadsheet lists, channel lists, folder lists, etc.) until the user has explicitly confirmed a connection for every app. This is a hard gate: no confirmation, no RPCs.
1. Extract connection requirements. Before checking connections, call extract_blueprint_components with the unconfigured blueprint (all modules placed, parameters empty). This returns the authoritative list of: which modules need connections, the connection type for each, and the required OAuth scopes. Use this output — not manual inspection of the Scenario Plan — as the definitive checklist. Builtin modules (builtin:BasicRouter, builtin:BasicFeeder, json:ParseJSON, etc.) do NOT need connections. AI agent modules (ai-local-agent:RunLocalAIAgent) are NOT builtin — they require an AI provider connection via makeConnectionId.
2. Check existing connections (with scope verification). Call connections_list with the target teamId. List all connections without a type filter first, then match by accountName in the results — the type filter matches accountName, NOT the Make app name (e.g., Google Sheets uses "google", Gmail uses "google-email", Slack uses "slack2" or "slack3").
Scope verification (CRITICAL for OAuth connections): For each matching connection, compare its scopes against the required scopes from extract_blueprint_components. A connection that authenticates successfully but lacks a required scope will cause 403/permission errors at runtime. If a matching connection exists but its scopes are insufficient, do NOT attempt to use it — either expand its scopes (see make-module-configuring skill, connections reference, Step 3a) or create a new connection with the correct scopes.
3. Ask the user to pick. For each app, present a numbered list and WAIT for the user's reply:
- If matching connections exist (even just one), list ALL of them with name, ID, metadata (email, workspace), and scope status (sufficient / insufficient). Always include "Create a new connection" as the last option:
I found these existing Google connections:
1. "Google - Marketing" (ID: 12345, email: marketing@acme.com) — scopes: sufficient
2. "Google - Personal" (ID: 12346, email: me@gmail.com) — scopes: insufficient (missing Google Drive file access)
3. Create a new connection
Which one should I use for Google Sheets?Even if there is only one match, still ask. Connections with insufficient scopes should be listed but flagged — offer scope expansion or new connection creation for those.
- If no matching connection exists, inform the user and create a credential request via
credential_requests_create, including the required scopes fromextract_blueprint_components.
4. Confirm all connections are ready. Do NOT proceed to Step 2 until every required connection has a user-confirmed connection ID.
Step 2: Configure Each Module
Configure modules left to right (upstream to downstream) following the make-module-configuring skill: 1. Read the module interface (app-module_get with instructions format) 2. Load dynamic field options via RPCs (now that connections are confirmed) 3. Fill parameters and mapper 4. Validate each module individually (validate_module_configuration)
Step 3: Validate the Blueprint
Call validate_blueprint_schema on the complete blueprint JSON to catch structural issues before submission.
Step 4: Create the Scenario
Call scenarios_create with the validated blueprint. The blueprint must include a top-level metadata object — see Blueprint Construction — Deployment Checklist for the required structure.
Scheduling type for webhook/instant trigger scenarios: When the first module is a webhook or instant trigger (listener: true), always use{"type": "immediately"}as the scheduling type when callingscenario_scheduling_update. Never use"indefinitely"for webhook scenarios — it causes scenario activation to fail with "Invalid interval." Scheduled (polling) scenarios should use"indefinitely"with an interval; webhook scenarios must use"immediately".
Step 5: Activate the Scenario
Newly created scenarios are inactive by default. Call scenarios_activate before attempting to run. Skipping this step causes scenarios_run to fail.
Step 6: Run & Verify
Run the scenario and confirm it succeeds before handing off to the user.
1. Execute. Call scenarios_run to trigger an immediate run.
2. Check the result. Call executions_list for the scenario, then executions_get on the most recent execution. Inspect the status field:
1= success — proceed to Step 7.3= error — continue to step 3.
3. Diagnose the failure. Read error.message and error.causeModule from the execution result. Common runtime issues that pass schema validation:
- Mapped fields resolving to
undefinedornullat runtime (e.g.,{{2.mimeType}}when the upstream module produced no output for that field) - Type conversion errors on optional parameters left at defaults
- Missing or expired connection tokens
4. Fix and retry. To update the scenario after diagnosis:
- Call
scenarios_deactivateon the scenario - Call
scenarios_updatewith the corrected blueprint - Call
scenarios_activateto re-enable - Call
scenarios_runagain and repeat from step 2
Repeat the diagnose-fix-retry cycle until the execution succeeds or the issue requires user intervention (e.g., missing input data, external service unavailable). If user action is needed, explain the error and what to do before retrying.
Step 7: Provide the Scenario URL
Always give the user the scenario URL after creation: https://<zone>.make.com/<teamId>/scenarios/<scenarioId> (uses team ID, not organization ID).
---
Core Concepts Reference
When composing scenarios, consult these feature docs to understand how Make's building blocks work. Read the relevant files before using these features in a module composition.
Foundational
- [Bundles](./bundles.md) — The unit of data flowing between modules. Understand bundle multiplicity before composing flows.
- [Mapping](./mapping.md) — Connecting data between modules. Field mapping, data types, collections, arrays, functions/formulas.
- [Connections](./connections.md) — Authenticating modules with external services. OAuth, API keys, connection reuse.
Triggers & Scenario Composition
- [Scheduling & Triggers](./scheduling-and-triggers.md) — How scenarios start: instant triggers, polling triggers, schedules, manual/on-demand.
- [Webhooks](./webhooks.md) — Instant triggers via HTTP endpoints. Custom webhooks and app-specific webhooks.
- [Subscenarios](./subscenarios.md) — Parent/child scenario composition. Sync and async calls, inputs/outputs, reuse.
Data Flow Patterns
- [Iterations](./iterations.md) — Processing arrays item-by-item. Implicit iterators, explicit Iterator, specialized iterators, Repeater.
- [Aggregations](./aggregations.md) — Collapsing multiple bundles into one. Array, Text, Numeric, and Table aggregators.
- [Data Stores](./data-stores.md) — Persistent key-value storage across scenario runs. Deduplication, state, cross-scenario data sharing.
Flow Control
- [Routing](./routing.md) — Router module: multiple routes, multiple can fire, cannot merge back. Fallback routes.
- [Branching](./branching.md) — If-Else module: mutually exclusive branches, can merge back.
- [Merging](./merging.md) — Merge module: converges If-Else branches into single flow.
- [Filtering](./filtering.md) — Input filters: pass/block bundles on conditions. Includes filter-vs-router decision guide.
Router vs If-Else Decision Guide
Choose If-Else + Merge when:
- Branches are mutually exclusive (only one should run per bundle)
- Branches need to converge into shared downstream modules (e.g., update a record, send a confirmation)
- The logic follows an "if A, do X; else if B, do Y; else do Z" pattern
Choose Router when:
- Multiple routes can fire for the same bundle (e.g., log to Sheets AND alert on Slack)
- Routes are independent endpoints with no shared follow-up steps
- You need parallel processing paths that don't converge
Advanced
- [AI Agents](./ai-agents.md) — Make AI Agents (New) with tool-calling. Module tools, scenario tools, MCP tools. Non-deterministic logic.
- [Error Handling](./error-handling.md) — Error handlers per module (Break, Commit, Ignore, Resume, Rollback). Throw module. Only suggest when user explicitly asks.
- [Blueprint Construction](./blueprint-construction.md) — Guidelines for building scenario blueprints programmatically via MCP.
- [Quick Patterns](./quick-patterns.md) — Compressed MCP call chains for common one-shot scenarios (Slack message, Google Sheets, Airtable, email).
Common App Gotchas
High-frequency configuration mistakes that cause silent failures or hard-to-diagnose runtime errors. Check these before finalizing module configuration in Phase 2 Step 2.
Google Sheets: valueInputOption Required for Write Modules
addRow and updateRow always require "valueInputOption": "USER_ENTERED" in the mapper (not parameters). Without it, the API returns 400: INVALID_ARGUMENT — 'valueInputOption' is required. There is no default — the field must be present:
"mapper": {
"valueInputOption": "USER_ENTERED",
"values": { "0": "{{1.name}}", "1": "{{1.email}}" }
}validate_module_configuration will catch this if called — this is exactly why validation is mandatory per module.
Google Sheets: Spreadsheet IDs from listSpreadsheets RPC
IDs returned by the listSpreadsheets RPC (e.g., 1abc123def456) must be prefixed with / when placed in the spreadsheetId parameter for mode: "select" / from: "drive" modules:
- Correct:
"/1abc123def456" - Wrong:
"1abc123def456"
Only applies to select-mode. Map-mode accepts the raw ID.
Webhook Scenarios: Scheduling Type
When the first module is a webhook (gateway:CustomWebHook or any instant trigger), always use {"type": "immediately"} for scheduling. Using "indefinitely" causes scenario activation to fail with "Invalid interval." See Step 4 above.
Gmail / Google Email: accountName Is "google-email", Not "google"
The google-email app (Gmail) uses a different connection type than Google Sheets, Calendar, and Drive. When filtering connections_list:
- Google Sheets / Calendar / Drive:
accountName: "google" - Gmail (
google-email):accountName: "google-email"
A generic "google" OAuth connection will NOT work for Gmail modules (google-email:sendAnEmail, google-email:TriggerNewEmail). It lacks the required Gmail scopes and uses a different connection type entirely. Always verify via extract_blueprint_components that you have the correct connection type — do not assume all Google apps share one connection.
IML Date Boundaries: No endOfDay() / startOfDay() Functions
IML does not have endOfDay(), startOfDay(), beginningOfDay(), or similar boundary functions. Attempting to use them produces an "Unknown function" error. To construct day boundaries, use formatDate to extract the date portion and concatenate a literal time:
Start of day: {{formatDate(now; "YYYY-MM-DD")}}T00:00:00Z
End of day: {{formatDate(now; "YYYY-MM-DD")}}T23:59:59ZThis is the one valid use of date + literal time concatenation. The general rule "never concatenate separate date and time strings" (see IML Expressions) applies to full ISO 8601 datetimes where both parts are dynamic — it does not prohibit combining a formatDate date-only result with a fixed literal time component.
Make AI Tools (ai-tools:Ask): Model Is Required, No Default
The model parameter in ai-tools:Ask (and other Make AI Toolkit modules) is required — there is no default value. Omitting it causes a 400 error at runtime. When using Make's AI Provider (ai-provider connection), use tier slug names: "small", "medium", or "large". (Older docs mention low/medium/high — these are stale; the runtime rejects them with Model X not allowed for Make AI Provider.) Only small is empirically verified for ai-tools:Summarize v2 as of 2026-05; the others follow Make UI conventions but should be confirmed via the Module dropdown. The dropdown labels surface as e.g. "SmallModel: gpt-5-nano. Reasoning: minimal." — match those slugs. Do not use provider-specific model IDs (e.g., "gpt-4o-mini") with the Make AI Provider — they are not valid tier names and will fail. The RpcGetModels RPC currently fails through the MCP server (org-context bug), so the model list cannot be queried programmatically — inspect the UI dropdown if unsure. See make-mcp-reference — Known MCP server bugs for the org-context bug.
No Make AI Provider connection? If the user has no ai-provider connection and cannot create one, check connections_list for alternative AI provider connections (openai-gpt-3, anthropic-claude, gemini-ai-*) and use the corresponding app-specific module instead of ai-tools:Ask. These modules accept provider-specific model IDs. See Blueprint Construction — AI Tools for details.
Official Documentation
Related Skills
- make-module-configuring — HOW to configure each module: parameters, connections, mapping, webhooks, data stores, IML expressions, validation
- make-mcp-reference — MCP server configuration, scopes, access control, and troubleshooting
Aggregations
What It Is
Aggregation is the inverse of iteration. It takes multiple bundles flowing through the scenario and collapses them into a single bundle. This is used when you need to collect processed results and combine them before continuing.
When to Use It
- You need to send one email/message containing all processed results (not one per item).
- You need to create a bulk payload (array of items) for an API call.
- You need to compute a sum, average, or concatenated string from multiple bundles.
- After an iteration, you want to "close the loop" and continue with a single bundle.
How It Works in Make
Aggregation Boundary
Every aggregator must be configured with a source module — the module that defines where aggregation starts. This is the aggregation boundary. All bundles produced from that point forward (until the aggregator) are collected into a single output bundle.
Aggregator Types
| Aggregator | App | Module slug | Output |
|---|---|---|---|
| Array Aggregator | built-in | builtin:BasicAggregator | Single bundle with an array property containing all collected items |
| Text Aggregator | Utils | util:TextAggregator | Single bundle with a string — all bundle contents concatenated |
| Numeric Aggregator | Utils | util:NumericalAggregator | Single bundle with a number — sum, average, etc. of a field across bundles |
| Table Aggregator | Utils | util:TableAggregator | Single bundle with structured table data |
Key Configuration Fields
- Source Module — The module where aggregation starts. Defines the aggregation boundary.
- Group By — Splits the aggregator's output into multiple bundles, one per distinct value of a formula. Each output bundle contains a
Key(the distinct value) and anArray(the aggregated data for that key). Useful for grouping results (e.g., aggregate invoices grouped by customer). - Target structure type (Array Aggregator only) — Defines the shape of the output array. Defaults to "Custom" (you pick which fields to include). If downstream modules are connected, the dropdown also offers their array-typed fields as targets, enabling auto-mapping.
- Stop processing after empty aggregation — When enabled, the aggregator produces no output if zero bundles reach it (e.g., all filtered out). The flow stops. When disabled (default), it outputs an empty result bundle.
Data Flow
Multiple bundles IN → [Aggregator] → Single bundle OUT (containing array/string/number)Flowchart Notation
Google Sheets - Search Rows (N bundles) → Transform Data (per row) → Array Aggregator → Slack - Send Message (single summary)Example
A scenario that lists all open invoices, transforms each, then sends a single summary email:
QuickBooks - List Invoices (N bundles) → Set Variable (extract fields) → Text Aggregator → Email - Send (one email with all invoice details)Gotchas
- Forgetting the Aggregator after iteration. If you iterate (or use an implicit iterator) and don't aggregate, ALL downstream modules execute N times. Always consider whether you need to "close the loop."
- Aggregation boundary matters. The source module setting determines which bundles get collected. Setting it wrong means the aggregator collects the wrong set of bundles.
- Choose the right aggregator type. Array Aggregator for structured data you'll process further. Text Aggregator for human-readable output. Numeric for calculations.
- Upstream data not forwarded. Bundles from the source module and intermediate modules are not passed forward by the aggregator. To preserve data from these bundles, explicitly include items in the aggregator's "Aggregated fields" configuration.
Official Documentation
See also: Iterations for the inverse operation, Bundles for bundle flow basics.
AI Agents
What It Is
Make AI Agents (New) are modules that sit within a Make scenario and provide non-deterministic, AI-driven logic. Unlike Router or If-Else (which use deterministic conditions), an AI agent module decides on its own which tools to call, how many times, and in what order — based on instructions and the incoming data.
When to Use It
Use this decision framework:
| Approach | When to use |
|---|---|
| Standard scenario (deterministic) | Predefined logic, same output for same input. E.g., syncing data, processing orders. |
| Scenario with AI app (structured AI) | Predefined logic + AI-generated content with specific parameters. E.g., translating, summarizing. |
| Scenario with AI agent (flexible AI) | Flexible reasoning, judgment calls, variable inputs/outputs. E.g., categorizing tickets, screening candidates. |
Since AI agents produce unpredictable results, choose tasks you'd trust an intern to handle. Avoid sensitive data, high-stakes decisions, or strict legal requirements.
How It Works in Make
1. Place a Make AI Agents (New) module in the scenario flow. 2. Configure an AI provider — connects the agent to an LLM (OpenAI, Anthropic, Gemini, or Make's built-in AI Provider). Free plan users must select Make's AI Provider; paid users can choose any supported provider. 3. Define instructions — the agent's role, goals, constraints, and step-by-step behavior. 4. Add Knowledge (optional) — files stored in the agent's long-term memory (FAQs, brand guidelines, company policies) that help it tailor responses. Knowledge files are stored in a RAG vector database, with relevant chunks retrieved based on requests. 5. Equip it with tools — the agent decides which tools to call at runtime. 6. At runtime, the agent receives the incoming bundle (input), processes it according to instructions, and decides which tools to call. It outputs a single bundle representing its result.
Tool Types
AI agents support three types of tools:
| Tool type | Description | When to use |
|---|---|---|
| Module tools | A single Make module attached as a tool. A scenario is auto-generated with all necessary inputs, outputs, and utility modules (may include 2-3 utility modules for I/O handling and aggregation for search-type modules). | Simple, quick — one module = one tool |
| Scenario tools | An entire Make scenario exposed as a tool. You define scenario inputs/outputs manually. Must end with "Return output" module to return data. Must be toggled "On demand" to activate. | Complex workflows needing multiple steps, filters, or specific I/O |
| MCP tools | Tools from an MCP server connected to the agent. Requires creating an MCP server connection with authentication. | External tool capabilities beyond Make's built-in modules |
Tool Discovery
When listing modules to use as agent tools, pass usage: "tool" to the app_modules_list MCP tool. This filters to only modules that are compatible as agent tools.
Flowchart Notation
Trigger: Webhook → AI Agent [tools: JIRA - List Tasks, JIRA - Create Task, JIRA - Update Task, Slack - Send Message]
(system prompt: "Triage incoming support requests, create or update JIRA tasks, notify on Slack")
→ Google Sheets - Log ResultExample
An agent that handles incoming customer inquiries by checking existing tickets, creating new ones if needed, and notifying the team:
Email - Watch Inbox → AI Agent [tools: Zendesk - Search Tickets, Zendesk - Create Ticket, Zendesk - Update Ticket, Slack - Post Message]
(system prompt: "For each email, check if a ticket exists. If yes, update it. If no, create one. Notify #support on Slack either way.")
→ Email - Send Auto-ReplyReal-Time Data Requires Tools
An AI agent only knows what its training data contains. If the scenario requires live data — current weather, stock prices, news headlines, live inventory — the agent must have a tool module configured that fetches that data at runtime.
Without a tool, the agent will either:
- Hallucinate plausible-sounding but stale or fabricated data, or
- Correctly refuse and report it cannot access live information.
Rule: Any time the agent needs data that changes over time or varies by user input (location, date, ticker symbol), attach the appropriate tool module. The agent will call it at runtime with the right arguments.
Example: A weather forecast agent needs a weather:ActionGetDailyForecast (or weather:ActionGetCurrentWeather) tool attached — without it, it cannot return real forecasts.
Known Model IDs
The RpcGetModels RPC often fails in MCP context due to org-level restrictions. When it does, use these known model IDs directly in defaultModel:
Google Gemini (gemini-ai-* connection)
| Model ID | Notes |
|---|---|
gemini-2.5-pro-preview-03-25 | Latest Gemini 2.5 Pro preview |
gemini-2.0-flash | Fast, efficient Gemini 2.0 |
gemini-1.5-pro | Stable Gemini 1.5 Pro |
gemini-1.5-flash | Fast Gemini 1.5 |
OpenAI (openai-gpt-3 connection)
| Model ID | Notes |
|---|---|
gpt-4o | Latest GPT-4o |
gpt-4o-mini | Smaller, faster GPT-4o |
gpt-4-turbo | GPT-4 Turbo |
Anthropic (anthropic-claude connection)
| Model ID | Notes |
|---|---|
claude-opus-4-5 | Most capable Claude |
claude-sonnet-4-5 | Balanced Claude |
claude-haiku-4-5 | Fast, lightweight Claude |
If RpcGetModels succeeds, prefer the returned list. If it fails, pick from the known IDs above based on the user's chosen provider.
Gotchas
- Non-deterministic. The agent may behave differently for similar inputs. If you need predictable, repeatable logic, use Router or If-Else instead.
- Module tools = one module each. For multi-step tool logic, use Scenario tools instead.
- Single bundle output. Regardless of how many tools the agent calls, it produces one output bundle.
- Cost and latency. AI agent modules call an LLM and potentially multiple tools. They are slower and more expensive than deterministic modules.
- Knowledge vs Input. Knowledge is persistent reference material (uploaded files). Input is per-execution data from the incoming bundle. Don't confuse the two. Text file input consumes significant memory; knowledge files are preferable for large documents.
- Live data needs tools. An agent without tools only has training data. If the task requires real-time or user-specific data, tools are mandatory — not optional.
- Knowledge file formats. Supported formats for knowledge upload: TXT, PDF, DOCX, CSV, MD, JSON. Token consumption varies by file size during vector conversion.
- Conversation memory. Leaving Conversation ID blank creates a new agent identity with each run — the agent has no memory of previous interactions.
- AI provider is locked at creation. Once an agent is created with a provider (OpenAI, Anthropic, Gemini, Make AI Provider), it cannot be changed. To switch providers, create a new agent.
- Agent deletion is permanent. Deleting an agent breaks all
Run an agentmodules that reference it. Verify no active scenarios depend on the agent before deleting. - Agents are team-shared. Like connections, agents are visible to all team members. For a private agent, use a team where you're the only member.
Official Documentation
- Make AI Agents (New)
- Introduction to AI Agents
- Create Your First AI Agent
- Sales Outreach AI Agent Use Case
- Create AI Agents for Different Triggers
- Knowledge
- Make AI Agents (New) App
- Make AI Agents (New) Best Practices
See also: Routing and Branching for deterministic alternatives.
For detailed module configuration (AI-decided fields, restore objects, tool setup, blueprint structure), see AI Agents Configuration.
Blueprint Construction
Guidelines for building scenario blueprints programmatically via MCP tools.
Blueprint Structure
Every blueprint is a JSON object with top-level keys: name, flow (array of modules), and metadata (scenario settings).
See examples/full-blueprint.json for a complete webhook → parse → Google Sheets blueprint.
The flow array contains modules executed in sequence. Each module can have nested flows via routes (for routers) or onerror (for error handlers).
Real-world template examples
The teaching blueprints above are deliberately stripped to highlight structure. When a real mapper expression, restore metadata block, scheduling shape, or production-shaped module config is needed, consult the top-10-by-usage public templates kept under examples/popular-templates/. Each file is the full Make API response (blueprint, controller, scheduling; most also include metadata.templateUrl and metadata.usage) — copy the relevant module config and adapt rather than reconstructing from scratch.
Match a planned scenario to the closest analogue:
Linear 2-module (trigger → action):
- 02-add-webhook-data-to-google-sheet.json —
gateway:CustomWebHook→google-sheets:addRow - 04-send-gmail-from-google-sheets-row.json —
google-sheets:watchRows→google-email:ActionSendEmail - 05-facebook-leads-to-google-sheets.json —
facebook-lead-ads:NewLeadMultiple→google-sheets:addRow - 06-whatsapp-basic-chatbot.json —
whatsapp-business-cloud:watchEvents→whatsapp-business-cloud:sendMessage - 07-incoming-emails-to-google-sheets.json —
email:TriggerNewEmail(IMAP) →google-sheets:addRow
Linear 3-module AI enrichment:
- 01-chatgpt-completions-from-google-sheets.json —
watchRows→openai-gpt-3:CreateCompletion→updateRow. Canonical reference for the row-level AI enrichment pattern, including{{1.\0\}}column mapping andmapper.rowNumber: "{{1.\__ROW_NUMBER__\}}"for write-back. - 03-chatgpt-telegram-bot.json —
telegram:WatchUpdates→openai-gpt-3:CreateCompletion→telegram:SendReplyMessage
Iterator (BasicFeeder):
- 09-save-gmail-attachments-to-drive.json —
google-email:TriggerNewEmail→builtin:BasicFeeder(iterates{{4.attachments}}) →google-drive:uploadAFile. The canonical shape for breaking an array bundle into per-item executions.
Router with branching/fanout:
- 08-summarize-website-and-create-social-posts.json —
browse-ai:onTaskFinished→builtin:BasicRouterfanning out to ChatGPT-summarize-then-post on LinkedIn and Facebook. Reference for multi-platform fanout where each route is independent. - 10-sync-notion-to-google-calendar.json —
notion:watchDatabaseItems→ router with create/update/delete branches against Google Calendar. Reference for state-routing on a single bundle (created vs. updated vs. archived).
When the user's request is a near-match for one of these, copy the module sequence and mapper/parameters shape directly, then swap connection IDs and resource IDs. When it diverges, treat the file as a structural reference for the modules it shares.
Before reusing any of these files as a blueprint, see `examples/popular-templates/README.md` — hardcoded resource IDs have been emptied to "" and need to be supplied, and real-world module IDs are often non-sequential and must be renumbered to satisfy the construction rules below.
Module Structure
{
"id": 1,
"module": "namespace:ModuleName",
"version": 1,
"parameters": {},
"mapper": {},
"filter": null,
"metadata": {
"designer": { "x": 0, "y": 0, "name": "Display Name" }
}
}Required Fields
- id — Unique positive integer. Assign sequentially starting from 1.
- module — Full identifier:
namespace:ModuleName(e.g.,google-sheets:watchRows,builtin:BasicRouter,json:ParseJSON). - Make Code module — Use
"module": "code:ExecuteCode". - version — Integer matching the app version from
app-modules_list. Always verify via the MCP tool.
Key Rules
- Webhook hooks: Do not pass a
hookobject withtype: "create"insideparametersforgateway:CustomWebHook. Leaveparametersempty — the webhook must be configured separately after scenario creation. - Module parameters: Parameters and mapper SHOULD be fully populated during blueprint construction. Use
app-module_get(instructions format) to discover required fields,rpc_executeto load dynamic options (spreadsheet lists, calendar lists, channel lists), andconnections_listto get connection IDs. Only leave fields empty when the value genuinely cannot be determined at build time. - Module version: Always specify the correct
versionmatching the app version fromapp-modules_list. - Flow IDs: Assign sequential integer IDs starting from 1. Each module in the flow needs a unique ID.
- Designer metadata: Include
metadata.designerwithxandycoordinates (increment x by 300 per module for horizontal layout). - Aggregator `feeder` parameter: When using
builtin:BasicAggregatororutil:TextAggregator, thefeederproperty (which links the aggregator to its source iterator/feeder module) goes inside `parameters`, not as a top-level module property. The value is theidof the source module that feeds bundles into this aggregator. Example:
{
"id": 3,
"module": "builtin:BasicAggregator",
"version": 1,
"parameters": { "feeder": 2 },
"mapper": {"properties": {}},
"metadata": {"designer": {"x": 600, "y": 0}}
}Parameters vs Mapper
- parameters — Fixed configuration values (connection settings, selected accounts, static options). Leave as
{}for modules that require post-creation configuration. - mapper — Dynamic field mappings using template expressions. Maps output from previous modules to current module inputs.
Designer Coordinates
Include metadata.designer with x and y coordinates for visual layout in the Make designer.
Convention:
- x — Increment by 300 per module in the horizontal flow direction.
- y — 0 for the main flow. Offset by 150–300 per route branch for routers.
Linear flow example:
Module 1: x=0, y=0
Module 2: x=300, y=0
Module 3: x=600, y=0Router with branches:
Router: x=600, y=450
Route 1: x=900, y=0
Route 2: x=900, y=300
Route 3: x=900, y=600
Route 4: x=900, y=900Filter Conditions
Filters control which bundles pass through to a module. A filter has a name and conditions.
Condition Structure
Conditions use a nested array structure: outer array = OR groups, inner array = AND conditions within each group.
{
"filter": {
"name": "Only active high-priority",
"conditions": [
[
{ "a": "{{1.status}}", "b": "ACTIVE", "o": "text:equal:ci" },
{ "a": "{{1.priority}}", "b": "high", "o": "text:equal" }
]
]
}
}Each condition object has:
- a — Left operand (typically a reference like
{{moduleId.fieldName}}) - b — Right operand (comparison value, optional for some operators)
- o — Operator string
Common Operators
| Operator | Description |
|---|---|
text:equal | Exact text match |
text:equal:ci | Case-insensitive text match |
text:contain | Text contains substring |
text:notequal | Text does not equal |
text:startswith | Text starts with |
text:endswith | Text ends with |
number:equal | Numeric equality |
number:greater | Greater than |
number:less | Less than |
boolean:equal | Boolean equality |
date:before | Date is before |
date:after | Date is after |
exist | Value exists (no b needed) |
notexist | Value does not exist (no b needed) |
Multiple OR Groups
To match bundles where status is ACTIVE or priority is urgent:
{
"conditions": [
[ { "a": "{{1.status}}", "b": "ACTIVE", "o": "text:equal" } ],
[ { "a": "{{1.priority}}", "b": "urgent", "o": "text:equal" } ]
]
}Mapper Expression Patterns
Mapper values use double-brace template expressions to reference data from previous modules.
Reference Syntax
| Pattern | Description | Example |
|---|---|---|
{{id.field}} | Module output field | {{1.email}} |
{{id.\col\}} | Spreadsheet column by index | {{1.\0\}} |
{{id.field[].subfield}} | Array iteration | {{1.choices[].message.content}} |
{{id.__IMTLENGTH__}} | Bundle count from module | {{6.__IMTLENGTH__}} |
{{id.__ROW_NUMBER__}} | Row number (Sheets) | {{1.__ROW_NUMBER__}} |
{{function(args)}} | Built-in function call | {{split(1.\1\; space)}} |
{{now}} | Current timestamp | {{now}} |
{{random}} | Random number (0-1) | {{random}} |
Common Functions
split(value; delimiter)— Split string into arrayformatDate(date; format)— Format date (e.g.,"YYYY-MM-DD")floor(number)— Round downlength(array)— Array lengthlower(text)— Lowercaseupper(text)— Uppercasetrim(text)— Trim whitespacetoString(value)— Convert to string
Router Patterns
Routers distribute bundles across multiple routes. Each route has its own flow of modules.
See examples/router-pattern.json for a router with Facebook and Twitter routes filtered by platform tag.
Multiple routes can fire for the same bundle — routers are not mutually exclusive.
If-Else Branching Pattern
If-Else branching uses two consecutive modules in the top-level flow: builtin:BasicIfElse immediately followed by builtin:BasicMerge. Branch subflows are nested inside the If-Else module's branches array — they do not appear in the top-level flow. See Branching for full details.
Router vs If-Else Decision Guide
| Router | If-Else | |
|---|---|---|
| Multiple branches can fire | Yes | No — first match only |
| Can merge back | No | Yes (via Merge module) |
| Use case | Parallel processing paths | Mutually exclusive logic |
Choose If-Else + Merge when:
- Branches are mutually exclusive (only one should run per bundle)
- Branches need to converge into shared downstream modules
- The logic follows "if A, do X; else if B, do Y; else do Z"
Choose Router when:
- Multiple routes can fire for the same bundle
- Routes are independent endpoints with no shared follow-up
See examples/if-else-pattern.json for the If-Else module paired with a Merge module (both if_else_module and merge_module keys).
Each branch requires:
- `type` —
"condition"(withconditions) or"else"(fallback, no conditions) - `merge` —
trueto converge back via the Merge module - `conditions` — OR/AND condition arrays (same format as filters). Only on
"condition"type branches. - `flow` — Array of modules for this branch
The Merge module requires:
- `filters` — Array with one entry per branch (use
nullfor no filter). Must match branch count. - `outputs` — Array of output definitions (typically
[])
The If-Else evaluates conditions in order, runs the first matching branch's subflow, then continues with the Merge module. Modules after Merge execute regardless of which branch was taken.
Iterator and Aggregator Pairs
For processing arrays item-by-item, use a Feeder → processing → Aggregator pattern.
{ "id": 15, "module": "builtin:BasicFeeder", "version": 1,
"mapper": { "array": "{{split(1.`1`; space)}}" } }After processing, collect results with feeder inside parameters (see Key Rules above):
{ "id": 16, "module": "builtin:BasicAggregator", "version": 1,
"parameters": { "feeder": 15 },
"mapper": { "properties": { "email": "{{15.value}}" } } }The feeder value is the source module's ID. It must be inside parameters — placing it as a top-level module property is ignored by the runtime.
Text aggregation uses util:TextAggregator — same feeder pattern:
{ "id": 10, "module": "util:TextAggregator", "version": 1,
"parameters": { "feeder": 8, "rowSeparator": "\n" },
"mapper": { "value": "{{8.description}}" } }Aggregator Metadata
Aggregators benefit from metadata.restore.extra which tells the Make UI which source module feeds them. Without it the aggregator works at runtime but the designer won't display the source module label correctly.
{
"metadata": {
"expect": [{"name": "value", "type": "text", "label": "Text"}],
"restore": {
"extra": {
"feeder": {
"label": "<Module Name> - <Module Name> [<module ID>]"
}
},
"parameters": {
"rowSeparator": { "label": "New row" }
}
},
"designer": { "x": 600, "y": 0, "name": "Text Aggregator" }
}
}- `restore.extra.feeder.label` — Human-readable label for the source module (displayed in the aggregator's "Source Module" dropdown in the designer). Format:
"<Module Name> - <Module Name> [<module ID>]"— e.g.,"Search Events - Search Events [1]". - `restore.parameters` — Labels for parameter values (e.g., row separator display name).
- `expect` — Declares the aggregator's input fields. For
util:TextAggregator, this is typically[{"name": "value", "type": "text", "label": "Text"}].
Global Scenario Metadata
The top-level metadata object configures scenario execution settings. Set defaults when creating blueprints:
{
"metadata": {
"instant": false,
"version": 1,
"scenario": {
"roundtrips": 1,
"maxErrors": 3,
"autoCommit": true,
"autoCommitTriggerLast": true,
"sequential": false,
"slots": null,
"confidential": false,
"dataloss": false,
"dlq": false,
"freshVariables": false
},
"designer": {
"orphans": []
},
"zone": "eu2.make.com"
}
}Key fields:
- instant —
trueif using an instant trigger (webhooks) - roundtrips — Number of execution cycles per run
- maxErrors — Error threshold before stopping.
Scenario URLs
After creating a scenario, provide the user with a direct link to edit it in the Make designer:
https://{zone}/{teamId}/scenarios/{scenarioId}/edit- zone — The Make.com datacenter zone (e.g.,
eu2.make.com), from the organization'szonefield. - teamId — The team ID (integer) where the scenario was created. Not the organization ID.
- scenarioId — The scenario ID returned by
scenarios_create.
Example: https://eu2.make.com/12345/scenarios/8879767/edit
Connection Parameters
Modules that connect to external services require a connection parameter with the connection ID. The most common field name is __IMTCONN__, but some modules use different names (e.g., makeConnectionId for ai-tools:Ask). Always verify the exact field name from app-module_get instructions output.
Look up existing connections via connections_list and place the ID in parameters:
{
"parameters": {
"__IMTCONN__": 12345
}
}Without a valid connection parameter, the scenario will be marked isinvalid: true and cannot be activated.
The metadata.parameters arrays are set by Make.com and store UI state for the designer. Populate metadata.restore only for aggregators (see Aggregator Metadata above).
Configuring Modules via RPC
Most modules have dynamic fields that require RPC calls to populate (e.g., selecting a spreadsheet, a calendar, a Slack channel). The pattern:
RPC Discovery Pattern
1. Read the module schema: Call app-module_get with outputFormat: "instructions" to get the full input interface. 2. Find RPC hints: The instructions will indicate which fields load their options dynamically (e.g., "select a spreadsheet" with an RPC endpoint). 3. Call `rpc_execute`: Execute the RPC to get the list of options. Always include __IMTCONN__ in the data parameter with the connection ID. 4. Use the returned values: Place the selected value in the correct domain (parameters for static selections, mapper for dynamic values).
Chained RPCs
Some modules require a chain of RPC calls where each selection narrows the next:
List spreadsheets → Select one → List sheets within it → Select one → Get column headersEach RPC in the chain requires the results of the previous one. Pass prior selections in the data parameter alongside __IMTCONN__.
RPC Data Fields
The data parameter for rpc_execute must always include:
- `__IMTCONN__` — The connection ID (required for any RPC that talks to an external service)
- Previous selections — Any upstream RPC results that narrow the current query (e.g.,
spreadsheetIdwhen listing sheets)
See make-module-configuring/general-principles.md for the full 5-phase configuration workflow including RPC resolution.
Common Configuration Gotchas
Google Sheets: Mode Selection
When using Google Sheets modules with mode: "select" and from: "drive", the spreadsheetId must be prefixed with /:
- Correct:
"/1abc123def456" - Incorrect:
"1abc123def456"
Parameters for watchRows/updateRow/addRow in select mode — all go in parameters (not mapper):
mode—"select"from—"drive"spreadsheetId—/-prefixed drive file IDsheetId— Sheet tab identifierincludesHeaders— Booleanlimit— Row limit (for triggers)tableFirstRow— First data row
Only use mode: "map" as a fallback when RPC-based selection is unavailable.
Google Calendar: duration Field
The duration field in Google Calendar modules can cause IML errors when used with expressions. Prefer using the end field with an IML function instead:
{{addHours(start; 1)}}Always provide either end OR duration, never neither. If using end, compute it from start using IML date functions.
IML Date Functions
addHours(date; N)— Add N hours to a dateaddMinutes(date; N)— Add N minutes to a dateaddDays(date; N)— Add N days to a dateformatDate(date; "YYYY-MM-DD")— Format a date string
Common Module Reference
Triggers
| Module | Description |
|---|---|
gateway:CustomWebHook | Receive webhook HTTP requests |
google-sheets:watchRows | Watch for new spreadsheet rows |
google-email:TriggerNewEmail | Watch for new emails |
Utility
| Module | Description |
|---|---|
builtin:BasicRouter | Route bundles to multiple paths (cannot merge back) |
builtin:BasicIfElse | Mutually exclusive branching (can merge back) |
builtin:BasicMerge | Converge If-Else branches into single flow |
builtin:BasicFeeder | Iterate over an array |
builtin:BasicAggregator | Collect bundles into one |
json:ParseJSON | Parse a JSON string |
util:TextAggregator | Concatenate text from bundles |
util:SetVariable | Set a scenario variable |
util:FunctionSleep | Pause execution |
http:ActionSendData | Make HTTP requests |
AI / LLM
| Module | Description |
|---|---|
ai-tools:Ask | Make AI Toolkit — Simple Text Prompt (connection field: makeConnectionId) |
Note:ai-tools:AskusesmakeConnectionIdinstead of__IMTCONN__for its connection parameter. The connection type isai-providerand model values are abstract tier names ("low","medium","high").
Make AI Tools: Model Parameter
The ai-tools:Ask module (and other Make AI Toolkit modules) use abstract model tier names, not provider-specific model IDs. The model parameter goes in parameters (static), not mapper.
`model` is required — there is no default. Omitting it causes a 400 error at runtime. Do not use provider-specific model IDs (e.g., "gpt-4o-mini", "claude-sonnet-4-5") with the Make AI Provider — only tier names are valid.
Known tier values for "makeConnectionId" with Make's AI Provider (ai-provider):
| Value | Label | Description |
|---|---|---|
"low" | Small/Fast model | Lightweight, fast, cheap — simple classification tasks |
"medium" | Medium model | Balanced — most text generation use cases (default choice) |
"high" | Large/Powerful model | Complex reasoning, long outputs |
The RpcGetModels RPC that normally populates this list fails via MCP due to an org-context limitation. Always use the tier names above directly — do not attempt to resolve the RPC.
{
"id": 2,
"module": "ai-tools:Ask",
"version": 2,
"parameters": {
"makeConnectionId": "<connection_id>",
"model": "medium"
},
"mapper": {
"input": "{{1.`0`}}"
}
}For custom AI provider connections (OpenAI, Anthropic), the model values are provider-specific IDs (e.g., "gpt-4o-mini", "claude-3-haiku-20240307"). Only the Make AI Provider uses tiers.
Fallback when Make AI Provider is unavailable: If the user has no ai-provider connection (or cannot create one due to plan limitations), check connections_list for alternative AI provider connections and use the corresponding app-specific module instead of ai-tools:Ask:
Connection accountName | App module alternative | Model ID format |
|---|---|---|
openai-gpt-3 | openai:CreateChatCompletion | Provider-specific: "gpt-4o", "gpt-4o-mini" |
anthropic-claude | Anthropic app modules | Provider-specific: "claude-sonnet-4-5", "claude-haiku-4-5" |
gemini-ai-* | Gemini app modules | Provider-specific: "gemini-2.0-flash", "gemini-1.5-pro" |
These modules use __IMTCONN__ (not makeConnectionId) and accept provider-specific model IDs. Call app_modules_list for the specific app to discover available modules and app-module_get for configuration details.
Google Sheets: valueInputOption for Write Modules
`valueInputOption` (updateRow / addRow): Always set "valueInputOption": "USER_ENTERED" in the mapper for write modules. This tells Google Sheets to interpret values as if typed by a user (numbers as numbers, dates as dates, formulas evaluated). Without it, values default to raw string insertion which can break date/number formatting. "RAW" is only appropriate when you explicitly want to prevent formula evaluation.
Error Directives
| Module | Description |
|---|---|
builtin:Resume | Resume with fallback output |
builtin:Commit | Commit and stop |
builtin:Rollback | Rollback all operations |
builtin:Ignore | Ignore error and continue |
builtin:Break | Move to incomplete executions |
Use the exact token builtin:Ignore. Do not document or search for a separate builtin:IgnoreError directive; that name is not the canonical Make blueprint directive.
Deployment Checklist
After constructing a blueprint, follow this sequence to deploy and run it:
1. Validate the blueprint — call validate_blueprint_schema to catch structural errors before submission. Note: this validator checks the static blueprint schema but may reject valid module-specific properties (e.g., aggregator metadata.expect or metadata.restore fields) that the runtime accepts. If validation fails on metadata or module-specific fields that you know are correct from working examples, proceed with scenarios_create — the runtime is the authoritative validator.
2. Ensure `metadata` is present — scenarios_create requires a top-level metadata object. Add the default metadata block (see Global Scenario Metadata above for the full structure). At minimum include:
{
"metadata": {
"version": 1,
"scenario": {
"roundtrips": 1,
"maxErrors": 3,
"autoCommit": true,
"autoCommitTriggerLast": true,
"sequential": false,
"confidential": false,
"dataloss": false,
"dlq": false,
"freshVariables": false
},
"designer": { "orphans": [] }
}
}3. Validate module parameters — for each module that has parameters, call validate_module_configuration with the module's app, version, and parameter values. This catches type mismatches (e.g., boolean false vs string "false"), missing required fields, and invalid enum values that validate_blueprint_schema does not check. See Phase 5 in make-module-configuring for the full validation workflow.
4. Create the scenario — call scenarios_create with the validated blueprint.
5. Configure scheduling — call scenario_scheduling_update with the appropriate scheduling object:
- Webhook / instant trigger (first module is
gateway:CustomWebHookor any module withlistener: true): use{"type": "immediately"}. Do not use `"indefinitely"` — it causes activation to fail with "Invalid interval." - Polling / scheduled trigger: use
{"type": "indefinitely", "interval": <seconds>}(minimum 900 for most accounts). - One-time scheduled run: use
{"type": "once", "date": "<ISO 8601 datetime>"}. Thedatefield requires full ISO 8601 datetime format — e.g.,"2026-04-11T09:00:00.000Z". A date-only string like"2026-04-11"fails with "should match format date-time". - On-demand / manual: omit scheduling or use
{"type": "on-demand"}.
6. Activate the scenario — newly created scenarios are inactive by default. Call scenarios_activate before attempting to run. Running an inactive scenario will fail.
7. Run and verify the scenario — call scenarios_run to execute immediately. Then call executions_list followed by executions_get on the latest execution to confirm status: 1 (success). If status: 3 (error), diagnose via error.message and error.causeModule, fix the blueprint, and re-deploy using the deactivate → scenarios_update → activate → run cycle. See SKILL.md Phase 2 Step 6 for the full procedure.
8. Provide the scenario URL — format: https://<zone>.make.com/<teamId>/scenarios/<scenarioId> (uses team ID, not organization ID)
Official Documentation
Branching
What It Is
Branching uses the If-Else module to split the scenario flow into conditional branches where only the first matching branch executes. This works like a programming if/else if/else statement — conditions are evaluated in order, and the first match wins.
When to Use It
- You need mutually exclusive logic: "If A, do X. Else if B, do Y. Otherwise, do Z."
- Only one path should execute per bundle.
- You want to converge the branches back into a single flow afterward — see Merging.
How It Works in Make
1. Place an If-Else module in the scenario flow. 2. Define conditions on each branch. Conditions are evaluated in order. 3. The first branch whose condition matches executes. All other branches are skipped. 4. Unlike Router, If-Else branches can be merged back using the Merge module, allowing the scenario to continue as a single flow after the conditional logic.
Blueprint Structure
Internally, If-Else branching is represented as two consecutive modules in the top-level flow array: builtin:BasicIfElse immediately followed by builtin:BasicMerge.
The If-Else module contains a branches array. Each branch has a label, an optional condition (same format as filter conditions — the Else branch omits it), and a flow array holding the subflow of modules executed when that branch matches. These subflows are nested inside the If-Else module, not in the top-level flow.
Execution: the If-Else evaluates branch conditions in order, picks the first match, runs that branch's subflow, then returns to the top-level flow and continues with the Merge module.
{
"name": "BRANCHING",
"flow": [
{
"id": 2,
"module": "util:BasicTrigger",
"version": 1,
"metadata": {
"designer": {
"x": 0,
"y": 150
}
}
},
{
"id": 3,
"module": "builtin:BasicIfElse",
"version": 1,
"mapper": null,
"metadata": {
"designer": {
"x": 300,
"y": 150
}
},
"branches": [
{
"merge": true,
"label": "Some Condition",
"type": "condition",
"flow": [
{
"id": 5,
"module": "util:FunctionIncrement",
"version": 1,
"parameters": {
"reset": "scenario"
},
"mapper": {},
"metadata": {
"designer": {
"x": 600,
"y": 0
},
"restore": {
"parameters": {
"reset": {
"label": "Never"
}
}
},
"parameters": [
{
"name": "reset",
"type": "select",
"label": "Reset a value",
"required": true,
"validate": {
"enum": [
"run",
"execution",
"scenario"
]
}
}
]
}
}
],
"conditions": [
[
{
"a": "a",
"o": "text:equal",
"b": "b"
}
]
]
},
{
"merge": true,
"disabled": false,
"label": "",
"type": "else",
"flow": [
{
"id": 6,
"module": "util:FunctionIncrement",
"version": 1,
"parameters": {
"reset": "scenario"
},
"mapper": {},
"metadata": {
"designer": {
"x": 600,
"y": 300
},
"restore": {
"parameters": {
"reset": {
"label": "Never"
}
}
},
"parameters": [
{
"name": "reset",
"type": "select",
"label": "Reset a value",
"required": true,
"validate": {
"enum": [
"run",
"execution",
"scenario"
]
}
}
]
}
}
]
}
]
},
{
"id": 8,
"module": "builtin:BasicMerge",
"version": 1,
"mapper": null,
"metadata": {
"designer": {
"x": 900,
"y": 150
}
},
"outputs": [
{
"name": "a",
"mappings": [
"b",
"c"
]
}
],
"filters": [
null,
null
]
},
{
"id": 9,
"module": "util:GetVariable2",
"version": 1,
"parameters": {},
"mapper": {
"name": "x"
},
"metadata": {
"designer": {
"x": 1200,
"y": 150
},
"restore": {},
"expect": [
{
"name": "name",
"type": "text",
"label": "Variable name",
"required": true
}
],
"interface": [
{
"name": "x",
"label": "x",
"type": "any"
}
]
}
}
],
"metadata": {
"instant": false,
"version": 1,
"scenario": {
"roundtrips": 1,
"maxErrors": 3,
"autoCommit": true,
"autoCommitTriggerLast": true,
"sequential": false,
"slots": null,
"confidential": false,
"dataloss": false,
"dlq": false,
"freshVariables": false
},
"designer": {
"orphans": []
},
"zone": "eu1.make.com",
"notes": []
}
}Key points:
- Branch subflows live inside
branches[].flow, not in the top-level flow array. - The Merge module sits in the top-level flow immediately after the If-Else module — modules after Merge run regardless of which branch was taken.
- The Else branch has no
conditionproperty.
Key Difference from Routing
| Router | If-Else | |
|---|---|---|
| Multiple branches can fire | Yes | No — first match only |
| Can merge back | No | Yes (via Merge module) |
| Use case | Parallel processing paths | Mutually exclusive logic |
| Can nest inside each other | Yes | No — cannot place Router or another If-Else after an If-Else |
Restrictions
- You cannot add a Router module or another If-Else module into the flow after an If-Else module (within the same If-Else scope).
- Each condition route has a label and a condition with one or more rules.
- The Else route has no condition — it runs when nothing else matches.
Flowchart Notation
Trigger: Webhook → If-Else
├─ If (type = "order"): Process Order → Create Invoice
├─ Else If (type = "return"): Process Return → Issue Refund
└─ Else: Log Unknown Type
→ Merge → Send Confirmation EmailExample
Handling different support ticket priorities:
Zendesk - Watch Tickets → If-Else
├─ If (priority = "urgent"): Slack - Alert On-Call → PagerDuty - Create Incident
├─ Else If (priority = "high"): Slack - Post to #support-high
└─ Else: Google Sheets - Add to Backlog
→ Merge → Zendesk - Update Ticket (mark as "triaged")Gotchas
- Order matters. The first matching condition wins. Place more specific conditions before general ones. The official docs emphasize: "Structure your conditions from most specific to least specific to ensure that the correct route runs."
- Only one branch executes. If you need multiple paths to fire for the same bundle, use Router instead — see Routing.
- Merge is optional. You don't have to merge branches back. Without Merge, each branch simply ends independently (similar to Router behavior).
- Do Nothing placeholder. If a condition route lacks modules before connecting to Merge, Make displays a "Do Nothing" module as a placeholder; data still passes through.
- Operations but no credits. Both If-Else and Merge modules consume operations but do not consume credits.
Official Documentation
See also: Routing for multiple concurrent paths, Merging for converging branches, Filtering for simple pass/block gates.
Bundles
What It Is
A bundle is the fundamental unit of data that flows through a Make scenario. Every piece of information transferred between modules is wrapped in a bundle. When a scenario executes, modules receive bundles as input, process them, and output bundles for the next module in the sequence.
Key Properties
- A module can return zero to N bundles on its output.
- A module that returns 1 bundle (e.g., "Create Customer") produces a single unit of data representing the created entity.
- A module that returns multiple bundles (e.g., "Search Rows") produces one bundle per result row. These modules behave as implicit iterators — see Iterations.
- Every downstream module in the scenario executes once per incoming bundle. If a search module returns 10 bundles, every subsequent module runs 10 times.
Why It Matters
Understanding bundle behavior is critical for scenario composition:
1. Execution multiplier: Placing a multi-bundle module early in the scenario means all downstream modules execute N times. This affects both performance and API call counts. 2. Iterator/Aggregator decisions: If a module already returns multiple bundles (implicit iterator), you do NOT need an explicit Iterator. If you need to collapse multiple bundles back into one, you need an Aggregator — see Aggregations. 3. Module metadata: Use the app-module_get MCP tool with format: JSON to check the _annotations field. This tells you whether a module returns multiple bundles (implicit iterator behavior).
Flowchart Notation
In flowchart notation, bundle flow is implicit. When a module returns multiple bundles, annotate it:
Google Sheets - Search Rows (N bundles) → Slack - Send Message (runs per bundle)Official Documentation
Connections
What It Is
A connection is Make's way of authenticating with an external service. Before a module can interact with an app (e.g., Google Sheets, Slack, Stripe), it needs a connection that provides the credentials and permissions.
When to Use It
- Most modules that interact with an external service require a connection (some public APIs like the Weather app do not).
- When setting up a new app in a scenario for the first time.
- When a scenario fails with authentication or authorization errors.
How It Works in Make
Connection Lifecycle
1. Create a connection — authenticate with the external service (OAuth flow, API key, or other method depending on the app). 2. Assign to modules — each module that uses the app references the connection. 3. Reuse across modules — multiple modules for the same app can share one connection. 4. Reuse across scenarios — connections are organization-level resources, shared across all scenarios in a team.
Authentication Methods
| Method | Description | Common apps |
|---|---|---|
| OAuth 2.0 | Redirects to the service for authorization. Tokens auto-refresh. | Google, Slack, HubSpot, Salesforce |
| API Key | Simple key-based auth. Entered directly. | OpenAI, Anthropic, many REST APIs |
| Basic Auth | Username/password pair. | Legacy APIs, some databases |
| Custom/Token | Service-specific token or configuration. | Webhooks, custom HTTP modules |
Connection Per Module
When composing a scenario, note which apps are involved. Each distinct app needs at least one connection. Multiple modules from the same app typically share a connection.
Flowchart Notation
Connections are not shown in flowcharts — they're implicit. When relevant, note which apps are involved:
Trigger: Google Sheets - Watch New Rows [connection: Google] → Slack - Send Message [connection: Slack]Example
A scenario using three different services (three connections needed):
Trigger: Stripe - Watch Events [connection: Stripe]
→ Google Sheets - Add Row [connection: Google]
→ Slack - Send Message [connection: Slack]Gotchas
- Connections are created before module configuration. A module can't be fully configured until its connection exists and is authorized.
- OAuth token expiry. OAuth connections auto-refresh tokens, but if the refresh token is revoked (e.g., password change, permission revocation), the connection breaks and needs reauthorization. Failed reauthorization can also result from browser pop-up blocking or temporary service outages.
- One connection per auth context. If you need to access different accounts of the same service (e.g., two different Google accounts), create separate connections.
- Permission scopes. Some apps require specific permission scopes. If a module fails with permission errors, the connection may need broader scopes — recreate it with the required permissions.
- Editing replaces all data. Editing a connection replaces the original data entirely — Make does not keep the original connection data. All credentials must be provided again, and all modules using that connection automatically receive updates.
- Deletion dependencies. Before deleting a connection, verify it is not active in scheduled scenarios. Webhooks using a connection must be deleted first.
- accountName vs app name mismatch. The
connections_listtypefilter matchesaccountName, not the Make app name. Google Sheets, Google Calendar, and Google Drive all useaccountName: "google". Gmail (`google-email`) uses `accountName: "google-email"` — it is a separate connection type from `"google"`. Slack modules useaccountName: "slack2", Notion usesaccountName: "notion2"or"notion3". Best practice: List all connections without a type filter, then match byaccountNamefield in the results. Filtering by app name (e.g.,type: "google-sheets") will return zero results. - Connection `userId` is the bot, not the human. OAuth connection metadata includes a
userIdthat identifies the authenticated bot or service account — not the human user. Never use it as a message recipient or target identity. Resolve actual user/channel/resource targets via the module's RPCs.
Official Documentation
See also: Webhooks for webhook-specific authentication, Error Handling for handling ConnectionError failures.
Contributing: Adding New Feature Files
This document describes how to add new core concept or feature documentation files to the make-scenario-building skill.
File Location
All feature files live in this directory: skills/make-scenario-building/
Template
Every feature file MUST follow this structure:
---
name: <feature-name>
description: <one-line description of the feature in Make context>
---
# <Feature Name>
## What It Is
<Brief explanation of the concept>
## When to Use It
<Conditions/scenarios where this feature is needed>
## How It Works in Make
<Mechanics: which modules are involved, how they connect, which MCP tools to use for discovery>
## Flowchart Notation
<How to represent this feature in the Phase 1 flowchart notation used in SKILL.md>
## Example
<Concrete example of a scenario using this feature>
## Gotchas
<Common pitfalls or non-obvious behaviors>Steps to Add a New Feature
1. Create a new .md file in this directory using the template above. 2. Use kebab-case for the filename (e.g., error-handling.md, ai-agents.md). 3. Fill in all sections. If a section doesn't apply, write "N/A" rather than omitting it. 4. Add a reference to the new file in SKILL.md under the Core Concepts section. 5. If the feature interacts closely with another feature (e.g., filtering + routing), add cross-references in both files.
Exemptions
blueprint-construction.md is a technical reference (JSON structure, field specs, deployment checklist) rather than a feature doc. It uses YAML frontmatter for discoverability but does not follow the standard section headings above.
Naming Conventions
- Core concepts (e.g., bundles) describe foundational data model elements.
- Features (e.g., iterations, routing) describe scenario-building capabilities with specific modules.
- Use the same name in the filename, the
namefrontmatter field, and the# Heading.
Cross-References
When a feature depends on or relates to another, link to it explicitly. Example:
See also: Iterations for the inverse operation.
Data Stores
What It Is
Data stores are Make's built-in persistent storage — a simple database that lets you store, retrieve, update, and delete records across scenario runs. They're useful for maintaining state, caching data, deduplication, and sharing data between scenarios.
When to Use It
- You need to remember data between scenario runs (e.g., "last processed ID", counters, lookup tables).
- You need to share data between different scenarios.
- You need deduplication — check if a record was already processed.
- You need a simple key-value or tabular store without an external database.
How It Works in Make
Data Store Structure
A data store has a defined schema (columns/fields) and stores records identified by a unique key. Maximum storage is based on your plan's monthly credits (divide monthly credits by 1,000 to get MB). Minimum per store: 1 MB. Maximum: 1,000 data stores per organization.
Key Modules
| Module | What it does |
|---|---|
| Data store > Add/replace a record | Upserts a record by key — creates if new, replaces if exists |
| Data store > Update a record | Updates specific fields of an existing record |
| Data store > Get a record | Retrieves a single record by key |
| Data store > Check existence | Returns true/false for whether a record exists by key |
| Data store > Search records | Returns multiple records matching a filter (implicit iterator — returns N bundles) |
| Data store > Delete a record | Removes a record by key |
| Data store > Delete all records | Clears the entire data store |
| Data store > Count records | Returns the total number of records |
Creating a Data Store
Data stores are created at the organization level (not per scenario). Use the scenario_datastore_create MCP tool or create them in Make before referencing them in scenarios.
Flowchart Notation
Trigger: Webhook → Data store - Get a record [key: email] → If-Else
├─ If (exists): Data store - Update a record
└─ Else: Data store - Add/replace a record → Email - Send WelcomeExample
Deduplication — only process new orders:
Shopify - Watch Orders → Data store - Get a record [key: order_id]
→ [filter: record does not exist] → Process Order → Data store - Add/replace a record [key: order_id, status: processed]Cross-scenario state sharing:
Scenario A: API - Fetch Token → Data store - Add/replace a record [key: "api_token", value: token, expires: timestamp]
Scenario B: Data store - Get a record [key: "api_token"] → API - Make Request (using stored token)Gotchas
- Storage limits. Total data storage depends on your plan. Minimum data store size is 1 MB. Monitor usage to avoid "Out of space" errors.
- Maximum record size. Individual records are limited to 15 MB.
- Search returns multiple bundles.
Search recordsis an implicit iterator — downstream modules execute once per matching record. Pair with an Aggregator if needed. - Not a database replacement. Data stores are for simple key-value/tabular storage. For complex queries, relations, or large datasets, use an external database.
- Key uniqueness. Each record must have a unique key.
Add/replacewill overwrite existing records with the same key. - Field renaming risk. Renaming a field in a data structure makes the original data inaccessible because Make uses a different column identifier. Workaround: create a new field, copy data, then empty the original.
- Type change behavior. When changing a field type, existing data retains its original type; only new data uses the new type. Use conversion functions like
parseDateto migrate existing values. - Deleted records are unrecoverable. The "Discard changes" function does not restore deletions.
Official Documentation
See also: Iterations for handling multi-bundle output from Search, Aggregations for collapsing search results.
Error Handling
What It Is
Error handling in Make provides try/catch-style recovery for modules that fail during scenario execution. You can attach an error handler to any module (except Routers and error handlers themselves) to control what happens when that module encounters an error.
IMPORTANT: This is an advanced feature. Do NOT proactively suggest error handlers unless the user explicitly asks for them. Adding error handling to basic scenarios adds unnecessary complexity.
When to Use It
- The user explicitly requests error handling, retry logic, or fault tolerance.
- A critical module in the scenario must not silently fail (e.g., payment processing, data sync).
- The user wants fallback behavior when a third-party API is unreliable.
How It Works in Make
Every module (except Routers and error handlers) can have an error handler attached. When the module fails, the error handler determines what happens next.
Error Handler Types
| Handler | What It Does | When to Use |
|---|---|---|
| Break | Stores incomplete execution, enables automatic or manual retry later | You want to pause and retry without losing progress |
| Commit | Stops execution but saves all changes made up to the failure point | You need to halt but preserve partial work |
| Ignore | Discards the error, continues processing subsequent bundles | The error is non-critical and shouldn't block other items |
| Resume | Substitutes a fallback value for the failed module's output, continues processing | You can provide a reasonable default when a module fails |
| Rollback | Stops execution and reverts all changes made by transactional modules | You need all-or-nothing consistency (undo everything on failure) |
Attachment
Error handlers are attached to individual modules, not to the scenario as a whole. Each module can have its own error handler with its own type and configuration.
Flowchart Notation
Trigger → Module A → Module B [error handler: Resume (fallback: empty string)] → Module COr for break/retry:
Trigger → Module A → Payment API [error handler: Break (retry after 15 min)] → Confirmation EmailExample
A scenario where payment processing has retry logic but logging failures are ignored:
Webhook → Stripe - Charge Customer [error handler: Break] → Google Sheets - Log Transaction [error handler: Ignore] → Slack - Notify TeamError Types
Make categorizes errors into types. Understanding which type you're dealing with helps choose the right handler:
| Error Type | Description | Common Handler |
|---|---|---|
| ConnectionError | Cannot connect to the third-party service | Break (retry later) |
| DataError | Invalid data (wrong format, missing required fields) | Resume (fallback) or Ignore |
| RuntimeError | Unexpected failure during execution | Break or Rollback |
| RateLimitError | API rate limit exceeded | Break (with exponential backoff) |
| InvalidConfigurationError | Module misconfigured | Fix config — no handler helps |
| MaxFileSizeExceededError | File too large | Resume (skip) or Ignore |
Throw Module
The Throw module intentionally raises an error in the scenario flow. Use it for validation — e.g., if data doesn't meet expected criteria, throw an error to trigger an upstream error handler or halt the scenario.
Exponential Backoff
For transient failures (API rate limits, temporary outages), the Break handler supports automatic retries with exponential backoff — progressively increasing wait times between retry attempts.
Break Handler Retry Mechanics
When Break triggers, the current execution is stored as an incomplete execution. These can be retried automatically (if configured) or manually. Break can store record IDs from the failed batch, enabling targeted retries on only the failed items.
Gotchas
- Don't over-use. Most scenarios don't need error handlers. Only add them when the user has a specific failure mode in mind.
- Handler scope. Each handler is per-module. There's no global "catch all errors" handler for the entire scenario.
- Break creates incomplete executions. These consume storage and need to be resolved (retried or discarded) eventually.
- Rollback requires ACID modules. The Rollback handler only reverts changes for modules that support transactions (marked with the ACID tag). Non-ACID modules cannot rollback, creating potential data inconsistency if downstream errors occur.
- Instant trigger error behavior. If a scenario starts with an instant trigger and the "Number of consecutive errors" setting is active, the scenario deactivates immediately upon the first error (the consecutive error count is ignored).
Official Documentation
See also: Make documentation on error handlers for detailed configuration.
{
"name": "Scenario name",
"flow": [
{
"id": 1,
"module": "gateway:CustomWebHook",
"version": 1,
"parameters": {},
"mapper": {},
"metadata": { "designer": { "x": 0, "y": 0, "name": "Webhook Trigger" } }
},
{
"id": 2,
"module": "json:ParseJSON",
"version": 1,
"parameters": {},
"mapper": { "json": "{{1.value}}" },
"metadata": { "designer": { "x": 300, "y": 0, "name": "Parse Payload" } }
},
{
"id": 3,
"module": "google-sheets:addRow",
"version": 2,
"parameters": {},
"mapper": { "mode": "select", "values": { "0": "{{2.name}}", "1": "{{2.email}}" }, "includesHeaders": true, "insertDataOption": "INSERT_ROWS", "valueInputOption": "USER_ENTERED" },
"metadata": { "designer": { "x": 600, "y": 0, "name": "Add to Sheet" } }
}
],
"metadata": {
"instant": true,
"version": 1,
"scenario": {
"roundtrips": 1,
"maxErrors": 3,
"autoCommit": true,
"autoCommitTriggerLast": true,
"sequential": false,
"confidential": false,
"dataloss": false,
"dlq": false,
"freshVariables": false
},
"designer": { "orphans": [] },
"zone": "eu2.make.com"
}
}{
"if_else_module": {
"id": 3,
"module": "builtin:BasicIfElse",
"version": 1,
"parameters": {},
"mapper": null,
"metadata": { "designer": { "x": 300, "y": 0 } },
"branches": [
{
"merge": true,
"label": "If (status = active)",
"type": "condition",
"conditions": [[ { "a": "{{1.status}}", "b": "active", "o": "text:equal" } ]],
"flow": [
{ "id": 4, "module": "slack:CreateMessage", "version": 4, "parameters": {}, "mapper": { "text": "Active" }, "metadata": { "designer": { "x": 600, "y": -150 } } }
]
},
{
"merge": true,
"label": "Else",
"type": "else",
"flow": [
{ "id": 5, "module": "google-sheets:addRow", "version": 2, "parameters": {}, "mapper": {}, "metadata": { "designer": { "x": 600, "y": 150 } } }
]
}
]
},
"merge_module": {
"id": 6,
"module": "builtin:BasicMerge",
"version": 1,
"parameters": {},
"mapper": null,
"metadata": { "designer": { "x": 900, "y": 0 } },
"outputs": [],
"filters": [null, null]
}
}{
"blueprint": {
"flow": [
{
"id": 1,
"mapper": {},
"module": "google-sheets:watchRows",
"version": 2,
"metadata": {
"restore": {
"parameters": {
"from": {
"label": "My Drive"
},
"mode": {
"label": "Search by path"
},
"sheetId": {
"label": "Sheet1"
},
"__IMTCONN__": {
"data": {
"scoped": "true",
"connection": "google"
},
"label": "My Google connection"
},
"spreadsheetId": {
"path": [
"Create new completions with OpenAI ChatGPT from new rows in Google Sheets"
]
},
"includesHeaders": {
"label": "Yes"
},
"valueRenderOption": {
"mode": "chose",
"label": "Formatted value"
},
"dateTimeRenderOption": {
"mode": "chose",
"label": "Formatted string"
}
}
},
"designer": {
"x": 0,
"y": 0,
"name": "Get inputs"
},
"parameters": [
{ "name": "__IMTCONN__", "type": "account:google", "label": "Connection", "required": true },
{ "name": "mode", "type": "select", "label": "Search Method", "required": true, "validate": { "enum": ["select", "fromAll", "map"] } },
{ "name": "includesHeaders", "type": "select", "label": "Table contains headers", "required": true, "validate": { "enum": [true, false] } },
{ "name": "limit", "type": "uinteger", "label": "Limit", "required": true },
{ "name": "valueRenderOption", "type": "select", "label": "Value render option", "validate": { "enum": ["FORMATTED_VALUE", "UNFORMATTED_VALUE", "FORMULA"] } },
{ "name": "dateTimeRenderOption", "type": "select", "label": "Date and time render option", "validate": { "enum": ["SERIAL_NUMBER", "FORMATTED_STRING"] } },
{ "name": "from", "type": "select", "label": "Drive", "required": true, "validate": { "enum": ["drive", "share", "team"] } },
{ "name": "spreadsheetId", "type": "file", "label": "Spreadsheet ID", "required": true },
{ "name": "sheetId", "type": "select", "label": "Sheet Name", "required": true },
{ "name": "tableFirstRow", "type": "text", "label": "Row with headers", "required": true }
]
},
"parameters": {
"from": "drive",
"mode": "select",
"limit": 1,
"sheetId": "Sheet1",
"spreadsheetId": "",
"tableFirstRow": "A1:Z1",
"includesHeaders": true,
"valueRenderOption": "FORMATTED_VALUE",
"dateTimeRenderOption": "FORMATTED_STRING"
}
},
{
"id": 2,
"mapper": {
"model": "gpt-4o-mini",
"top_p": "1",
"select": "chat",
"messages": [
{
"role": "user",
"content": "You are a SEO expert.\n\nWhat are 5 effective SEO keywords that could be used to optimize an article about the {{1.`0`}}?",
"imageDetail": "auto"
}
],
"max_tokens": "1000",
"temperature": "1",
"n_completions": "1",
"response_format": "text"
},
"module": "openai-gpt-3:CreateCompletion",
"version": 1,
"metadata": {
"designer": {
"x": 300,
"y": 0,
"name": "Generate ChatGPT completion"
},
"parameters": [
{ "name": "__IMTCONN__", "type": "account:openai-gpt-3", "label": "Connection", "required": true }
]
},
"parameters": {}
},
{
"id": 3,
"mapper": {
"mode": "map",
"values": {
"0": "{{2.result}}"
},
"sheetId": "{{1.`__SHEET__`}}",
"rowNumber": "{{1.`__ROW_NUMBER__`}}",
"spreadsheetId": "{{1.`__SPREADSHEET_ID__`}}",
"tableFirstRow": "A1:Z1",
"valueInputOption": "USER_ENTERED"
},
"module": "google-sheets:updateRow",
"version": 2,
"metadata": {
"designer": {
"x": 600,
"y": 0,
"name": "Paste completion"
},
"parameters": [
{ "name": "__IMTCONN__", "type": "account:google", "label": "Connection", "required": true }
]
},
"parameters": {}
}
],
"metadata": {
"instant": false,
"version": 1,
"designer": {
"orphans": []
},
"scenario": {
"dlq": false,
"slots": null,
"dataloss": false,
"maxErrors": 3,
"autoCommit": true,
"roundtrips": 1,
"sequential": false,
"confidential": false,
"freshVariables": false,
"autoCommitTriggerLast": true
}
}
},
"controller": {
"name": "Generate ChatGPT Completions from Google Sheets Rows",
"description": "Effortlessly enhance your content creation process by integrating ChatGPT with Google Sheets. This powerful template allows you to automatically generate relevant completions, ranging from SEO keywords to custom responses, boosting productivity and content quality with ease."
},
"scheduling": {
"type": "indefinitely",
"interval": 900
},
"language": "en",
"metadata": {
"templateUrl": "10570-create-new-completions-with-openai-chatgpt-from-new-rows-in-google-sheets",
"usage": 107031
}
}
{
"blueprint": {
"flow": [
{
"id": 1,
"mapper": {},
"module": "gateway:CustomWebHook",
"version": 1,
"metadata": {
"restore": {
"parameters": {
"hook": {
"data": { "editable": "true" },
"label": "My gateway-webhook webhook"
}
}
},
"designer": { "x": 0, "y": 0 },
"parameters": [
{ "name": "hook", "type": "hook:gateway-webhook", "label": "Webhook", "required": true },
{ "name": "maxResults", "type": "number", "label": "Maximum number of results" }
]
},
"parameters": {
"maxResults": 2
}
},
{
"id": 2,
"mapper": {
"from": "drive",
"mode": "select",
"values": {},
"sheetId": "Sheet1",
"spreadsheetId": "",
"includesHeaders": true,
"insertDataOption": "INSERT_ROWS",
"valueInputOption": "USER_ENTERED",
"insertUnformatted": false
},
"module": "google-sheets:addRow",
"version": 2,
"metadata": {
"designer": { "x": 300, "y": 0 },
"parameters": [
{ "name": "__IMTCONN__", "type": "account:google", "label": "Connection", "required": true }
]
},
"parameters": {}
}
],
"metadata": {
"instant": false,
"version": 1,
"designer": { "orphans": [] },
"scenario": {
"dlq": false,
"slots": null,
"dataloss": false,
"maxErrors": 3,
"autoCommit": true,
"roundtrips": 1,
"sequential": false,
"confidential": false,
"freshVariables": false,
"autoCommitTriggerLast": true
}
}
},
"controller": {
"name": "Add webhook data to a Google Sheet",
"description": "Use this automation to instantly capture data from a webhook and automatically add it as a new row in a Google Sheet. This is ideal for real-time data logging, form submissions, or any event-driven updates that must be stored in a spreadsheet."
},
"scheduling": {
"type": "immediately",
"interval": 900
},
"language": "en",
"metadata": {
"templateUrl": "5893-add-data-to-a-google-sheet-received-from-a-webhook",
"usage": 39913
}
}
{
"blueprint": {
"flow": [
{
"id": 1,
"mapper": {},
"module": "telegram:WatchUpdates",
"version": 1,
"metadata": {
"restore": {
"parameters": {
"__IMTHOOK__": {
"data": {},
"label": "Choose a hook"
}
}
},
"designer": { "x": 0, "y": 0 },
"parameters": [
{ "name": "__IMTHOOK__", "type": "hook:telegramapi", "label": "Webhook" }
]
},
"parameters": {}
},
{
"id": 2,
"mapper": {
"model": "gpt-3.5-turbo",
"top_p": "1",
"select": "chat",
"messages": [
{
"role": "user",
"content": "Reply to this message: {{1.channel_post.text}}\nIf the message is about {topic}, provide all the relevant details related to the message. If the message is about anything other {topic} like golf, video games etc, reply by saying that you do not answer any other topic besides {topic}."
}
],
"max_tokens": "1000",
"temperature": "1",
"n_completions": "1",
"response_format": "text"
},
"module": "openai-gpt-3:CreateCompletion",
"version": 1,
"metadata": {
"designer": { "x": 300, "y": 0 },
"parameters": [
{ "name": "__IMTCONN__", "type": "account:openai-gpt-3", "label": "Connection", "required": true }
]
},
"parameters": {}
},
{
"id": 3,
"mapper": {
"text": "{{2.result}}",
"chatId": "{{1.channel_post.chat.id}}",
"parseMode": "",
"replyMarkup": "",
"messageThreadId": "",
"replyToMessageId": "",
"replyMarkupAssembleType": "reply_markup_enter"
},
"module": "telegram:SendReplyMessage",
"version": 1,
"metadata": {
"designer": { "x": 600, "y": 0 },
"parameters": [
{ "name": "__IMTCONN__", "type": "account:telegram", "label": "Connection", "required": true }
]
},
"parameters": {}
}
],
"metadata": {
"instant": false,
"version": 1,
"designer": { "orphans": [] },
"scenario": {
"dlq": false,
"slots": null,
"dataloss": false,
"maxErrors": 3,
"autoCommit": true,
"roundtrips": 1,
"sequential": false,
"confidential": false,
"freshVariables": false,
"autoCommitTriggerLast": true
}
}
},
"controller": {
"name": "ChatGPT-Powered Telegram Bot for Instant Replies",
"description": "Elevate your Telegram channel with our ChatGPT-powered bot. Deliver instant, smart responses to user queries, enhancing engagement and customer satisfaction effortlessly."
},
"scheduling": {
"type": "immediately"
},
"language": "en",
"metadata": {
"templateUrl": "10813-generate-telegram-bot-responses-with-ai",
"usage": 39268
}
}
{
"blueprint": {
"flow": [
{
"id": 1,
"mapper": {},
"module": "google-sheets:watchRows",
"version": 2,
"metadata": {
"restore": {
"valueRenderOption": { "mode": "chose", "label": "Formatted value" },
"dateTimeRenderOption": { "mode": "chose", "label": "Formatted string" }
},
"designer": { "x": 0, "y": 0 },
"parameters": [
{ "name": "__IMTCONN__", "type": "account", "label": "Connection", "required": true },
{ "name": "spreadsheetId", "type": "select", "label": "Spreadsheet", "required": true },
{ "name": "includesHeaders", "type": "select", "label": "Table contains headers", "required": true, "validate": { "enum": [true, false] } },
{ "name": "valueRenderOption", "type": "select", "label": "Value render option", "validate": { "enum": ["FORMATTED_VALUE", "UNFORMATTED_VALUE", "FORMULA"] } },
{ "name": "dateTimeRenderOption", "type": "select", "label": "Date and time render option", "validate": { "enum": ["SERIAL_NUMBER", "FORMATTED_STRING"] } },
{ "name": "limit", "type": "uinteger", "label": "Limit", "required": true },
{ "name": "tableFirstRow", "type": "text", "label": "First table row", "required": true },
{ "name": "sheetId", "type": "select", "label": "Sheet", "required": true }
]
},
"parameters": {
"limit": 2,
"valueRenderOption": "FORMATTED_VALUE",
"dateTimeRenderOption": "FORMATTED_STRING"
}
},
{
"id": 2,
"mapper": {
"cc": [],
"to": ["{{1.`0`}}"],
"bcc": [],
"from": "",
"html": "{{1.`2`}}",
"subject": "{{1.`1`}}",
"attachments": []
},
"module": "google-email:ActionSendEmail",
"version": 2,
"metadata": {
"designer": { "x": 300, "y": 0 },
"parameters": [
{ "name": "account", "type": "account:google-restricted", "label": "Connection" }
]
},
"parameters": {}
}
],
"metadata": {
"instant": false,
"version": 1,
"designer": { "orphans": [] },
"scenario": {
"dlq": false,
"dataloss": false,
"maxErrors": 3,
"autoCommit": true,
"roundtrips": 1,
"sequential": false,
"confidential": false,
"freshVariables": false,
"autoCommitTriggerLast": true
}
}
},
"controller": {
"name": "Send a Gmail email from a new Google Sheets row",
"description": "Effortlessly send Gmail messages when a new row is added to your Google Sheets. Spreadsheet must include columns: Email Address, Subject, Content."
},
"scheduling": {
"type": "indefinitely",
"interval": 900
},
"language": "en",
"metadata": {
"templateUrl": "5770-send-a-gmail-email-from-a-new-google-sheets-row",
"usage": 37832
}
}
{
"blueprint": {
"flow": [
{
"id": 1,
"mapper": {},
"module": "facebook-lead-ads:NewLeadMultiple",
"version": 2,
"metadata": {
"restore": {
"parameters": {
"__IMTHOOK__": {
"data": {},
"label": "Choose a hook"
}
}
},
"designer": { "x": 0, "y": 0 },
"parameters": [
{ "name": "__IMTHOOK__", "type": "hook:facebook-lead-ads-new-event", "label": "Webhook" }
]
},
"parameters": {}
},
{
"id": 2,
"mapper": {
"from": "drive",
"mode": "select",
"values": {},
"sheetId": "Sheet1",
"spreadsheetId": "",
"includesHeaders": true,
"insertDataOption": "INSERT_ROWS",
"valueInputOption": "USER_ENTERED",
"insertUnformatted": false
},
"module": "google-sheets:addRow",
"version": 2,
"metadata": {
"designer": { "x": 300, "y": 0 },
"parameters": [
{ "name": "__IMTCONN__", "type": "account:google", "label": "Connection", "required": true }
]
},
"parameters": {}
}
],
"metadata": {
"instant": false,
"version": 1,
"designer": { "orphans": [] },
"scenario": {
"dlq": false,
"slots": null,
"dataloss": false,
"maxErrors": 3,
"autoCommit": true,
"roundtrips": 1,
"sequential": false,
"confidential": false,
"freshVariables": false,
"autoCommitTriggerLast": true
}
}
},
"controller": {
"name": "Sync Facebook Lead Ads leads with Google Sheets",
"description": "Seamlessly capture and organize new Facebook Lead Ads leads by automatically adding them to a Google Sheets spreadsheet."
},
"scheduling": {
"type": "immediately",
"interval": 900
},
"language": "en",
"metadata": {
"templateUrl": "4219-save-new-facebook-lead-ads-leads-into-google-sheets",
"usage": 37283
}
}
{
"blueprint": {
"flow": [
{
"id": 1,
"mapper": {},
"module": "whatsapp-business-cloud:watchEvents",
"version": 1,
"metadata": {
"restore": {
"parameters": {
"__IMTHOOK__": {
"data": { "editable": "false" },
"label": "allUnchecked"
}
}
},
"designer": { "x": 0, "y": 0 },
"parameters": [
{ "name": "__IMTHOOK__", "type": "hook:whatsapp-business-cloud2", "label": "Webhook", "required": true }
]
},
"parameters": {}
},
{
"id": 2,
"mapper": {
"to": "{{1.contacts[].wa_id}}",
"text": {
"body": "Thanks for your message! I'm a basic chatbot! To make my answers more sophisticated, you can use routers, filters or send http requests to your own scenarios with webhooks.",
"preview_url": false
},
"type": "text"
},
"module": "whatsapp-business-cloud:sendMessage",
"version": 1,
"metadata": {
"designer": { "x": 300, "y": 0 },
"parameters": [
{ "name": "__IMTCONN__", "type": "account:whatsapp-business-cloud2", "label": "Connection", "required": true }
]
},
"parameters": {}
}
],
"metadata": {
"instant": false,
"version": 1,
"designer": { "orphans": [] },
"scenario": {
"dlq": false,
"dataloss": false,
"maxErrors": 3,
"autoCommit": true,
"roundtrips": 1,
"sequential": false,
"confidential": false,
"autoCommitTriggerLast": true
}
}
},
"controller": {
"name": "Create a basic chatbot for WhatsApp",
"description": "This template shows how to create a basic chatbot for WhatsApp. Every time a new message is received, Make will automatically answer it with the Send a Message module. To create more complex chatbots, you can add routers and filters."
},
"scheduling": {
"type": "immediately"
},
"language": "en",
"metadata": {
"templateUrl": "10260-create-a-basic-chatbot-for-whatsapp",
"usage": 35956
}
}
{
"blueprint": {
"flow": [
{
"id": 6,
"mapper": {},
"module": "email:TriggerNewEmail",
"version": 7,
"metadata": {
"restore": {
"parameters": {
"folder": { "path": ["INBOX"] },
"account": {
"data": { "scoped": "true", "connection": "imap" },
"label": "My Others (IMAP) connection"
},
"criteria": { "label": "Only Unread emails" }
}
},
"designer": { "x": 0, "y": 0 },
"parameters": [
{ "name": "account", "type": "account:imap,google-restricted,microsoft-smtp-imap", "label": "Connection", "required": true },
{ "name": "criteria", "type": "select", "label": "Criteria", "required": true, "validate": { "enum": ["ALL", "SEEN", "UNSEEN"] } },
{ "name": "from", "type": "email", "label": "Sender email address" },
{ "name": "to", "type": "email", "label": "Recipient email address" },
{ "name": "subject", "type": "text", "label": "Subject" },
{ "name": "text", "type": "text", "label": "Phrase" },
{ "name": "markSeen", "type": "boolean", "label": "Mark message(s) as read when fetched" },
{ "name": "maxResults", "type": "number", "label": "Maximum number of results" },
{ "type": "hidden" },
{ "name": "folder", "type": "folder", "label": "Folder", "required": true }
]
},
"parameters": {
"to": "",
"from": "",
"text": "",
"folder": "INBOX",
"subject": "",
"markSeen": false
}
},
{
"id": 5,
"mapper": {
"mode": "fromAll",
"values": {
"0": "{{now}}",
"1": "{{6.from.name}}",
"2": "{{6.subject}}",
"3": "{{6.from.address}}"
},
"sheetId": "Sheet1",
"spreadsheetId": "",
"includesHeaders": true,
"insertDataOption": "INSERT_ROWS",
"valueInputOption": "USER_ENTERED",
"insertUnformatted": false
},
"module": "google-sheets:addRow",
"version": 2,
"metadata": {
"designer": { "x": 300, "y": 0 },
"parameters": [
{ "name": "__IMTCONN__", "type": "account:google", "label": "Connection", "required": true }
]
},
"parameters": {}
}
],
"metadata": {
"instant": false,
"version": 1,
"designer": { "orphans": [] },
"scenario": {
"dlq": false,
"slots": null,
"dataloss": false,
"maxErrors": 3,
"autoCommit": true,
"roundtrips": 1,
"sequential": false,
"confidential": false,
"freshVariables": false,
"autoCommitTriggerLast": true
}
}
},
"controller": {
"name": "Add new incoming emails to a Google Sheets spreadsheet as a new row",
"description": "Every time you receive a new email, Make will automatically add selected data from the email to a new row in a Google Sheets spreadsheet."
},
"scheduling": {
"type": "indefinitely",
"interval": 900
},
"language": "en",
"metadata": {
"templateUrl": "2-add-new-incoming-emails-to-a-google-sheets-spreadsheet-as-a-new-row",
"usage": 22537
}
}
{
"blueprint": {
"flow": [
{
"id": 4,
"mapper": {},
"module": "google-email:TriggerNewEmail",
"version": 2,
"metadata": {
"restore": {
"searchType": { "label": "Simple filter" }
},
"designer": { "x": 0, "y": 0 },
"parameters": [
{ "name": "account", "type": "account", "label": "Connection", "required": true },
{ "name": "searchType", "type": "select", "label": "Filter type", "required": true, "validate": { "enum": ["simple", "gmail"] } },
{ "name": "markSeen", "type": "boolean", "label": "Mark email message(s) as read when fetched" },
{ "name": "maxResults", "type": "uinteger", "label": "Maximum number of results" },
{ "name": "criteria", "type": "select", "label": "Criteria", "required": true, "validate": { "enum": ["ALL", "SEEN", "UNSEEN"] } },
{ "name": "from", "type": "email", "label": "Sender email address" },
{ "name": "subject", "type": "text", "label": "Subject" },
{ "name": "text", "type": "text", "label": "Search phrase" },
{ "name": "folder", "type": "folder", "label": "Folder", "required": true }
]
},
"parameters": {
"from": null,
"text": "",
"subject": "",
"markSeen": false,
"maxResults": 2,
"searchType": "simple"
}
},
{
"id": 5,
"mapper": {
"array": "{{4.attachments}}"
},
"module": "builtin:BasicFeeder",
"version": 1,
"metadata": {
"designer": { "x": 300, "y": 0 }
},
"parameters": {}
},
{
"id": 6,
"mapper": {
"data": "{{5.data}}",
"select": "value",
"filename": "{{5.fileName}}"
},
"module": "google-drive:uploadAFile",
"version": 4,
"metadata": {
"designer": { "x": 600, "y": 0 },
"parameters": [
{ "name": "__IMTCONN__", "type": "account", "label": "Connection", "required": true }
]
},
"parameters": {}
}
],
"metadata": {
"instant": false,
"version": 1,
"designer": { "orphans": [] },
"scenario": {
"dlq": false,
"dataloss": false,
"maxErrors": 3,
"autoCommit": true,
"roundtrips": 1,
"sequential": false,
"confidential": false,
"freshVariables": false,
"autoCommitTriggerLast": true
}
}
},
"controller": {
"name": "Save new Gmail attachments to Google Drive",
"description": "Automatically save incoming Gmail attachments to your Google Drive. Demonstrates the Iterator pattern (BasicFeeder) for processing arrays of attachments one at a time."
},
"scheduling": {
"type": "indefinitely",
"interval": 900
},
"language": "en",
"metadata": {
"templateUrl": "1686-save-new-gmail-attachments-to-google-drive",
"usage": 20473
}
}
Popular templates — snapshots, not drop-in blueprints
These 10 files are lightly sanitized snapshots of public-templates_get-blueprint responses for the most-used public Make templates, kept here as canonical references for real mapper, parameters, restore, and scheduling shapes. Hardcoded resource IDs (spreadsheetId, page_id, channel IDs, etc.) have been blanked to empty strings (see below); everything else — including the original publisher's connection labels and organization labels under metadata.restore.* — is preserved verbatim so readers can see the canonical shape returned by the public endpoint. They are not ready-to-import blueprints.
Before reusing as a scenario blueprint
1. Fill in the emptied resource IDs. Hardcoded resource IDs (spreadsheetId, page_id, organization URN, calendar IDs, channel IDs, etc.) have been replaced with empty strings ("") because they belonged to the original template publisher. Replace each empty value with your own resource ID, or — for downstream modules — wire it from an upstream module's output using a mapper expression (see the {{1.\__SPREADSHEET_ID__\}} pattern in 01-chatgpt-completions-from-google-sheets.json module 3).
2. Re-select connections. Every module that needs auth carries a connection parameter — most commonly __IMTCONN__, but some module schemas name it differently (e.g., account in google-email:ActionSendEmail, see template 04). The exact name is listed in that module's metadata.parameters[] entry with a type starting account: (e.g., account:google-restricted). The corresponding metadata.restore.parameters.<name>.label carries the original publisher's connection label verbatim (e.g., "My Google connection", "My LinkedIn connection (integromat Developer)"); use it to identify which connection slot maps to which provider, then replace with your own connection IDs (connections_list → pick by accountName) before deploying.
3. Renumber module IDs when adapting. Real templates often have non-1-based or non-sequential IDs (e.g., template 07 flows id: 6 → id: 5; template 09 starts at id: 4) because of edit history in the original scenario. The project rule in `blueprint-construction.md` (sequential, starting at 1) applies when constructing new blueprints. When reusing a snippet from here, renumber the modules and update every mapper reference ({{4.attachments}}, {{1.\0\}}, etc.) accordingly.
4. Replace placeholder content. Some templates carry sample prompts, default LinkedIn/Facebook copy, example URLs (https://www.example.com), or even publisher typos in placeholders (e.g., Content: {[Insert Content} in template 08). Treat all human-readable strings inside mapper.* as starter text, not production copy — fix typos when adapting, not in the snapshot (a refresh would re-introduce them).
What stays verbatim
blueprint,controller,schedulingshapes — these define the structural patterns the docs reference.- All
moduleidentifiers,versionnumbers, andmetadata.designercoordinates. metadata.templateUrlandmetadata.usagewhere present — these document which public template the file was sourced from and its install count.
How to refresh a snapshot
Call public-templates_get-blueprint with the template ID encoded in metadata.templateUrl (the leading number, e.g., 10570 for template 01) and replace the file contents. Then re-apply the sanitization listed above.
{
"id": 5,
"module": "builtin:BasicRouter",
"version": 1,
"parameters": {},
"mapper": null,
"metadata": { "designer": { "x": 600, "y": 450 } },
"routes": [
{
"flow": [
{
"id": 7,
"module": "facebook-pages:CreatePost",
"version": 6,
"parameters": {},
"filter": {
"name": "Facebook posts only",
"conditions": [[ { "a": "{{6.`0`}}", "b": "FB", "o": "text:contain" } ]]
},
"mapper": { "message": "{{6.`1`}}" },
"metadata": { "designer": { "x": 900, "y": 0 } }
}
]
},
{
"flow": [
{
"id": 20,
"module": "twitter:createATweet",
"version": 6,
"parameters": {},
"filter": {
"name": "Twitter posts only",
"conditions": [[ { "a": "{{6.`0`}}", "b": "T", "o": "text:contain" } ]]
},
"mapper": { "text": "{{6.`1`}}" },
"metadata": { "designer": { "x": 900, "y": 300 } }
}
]
}
]
}Filtering
What It Is
A filter is a condition placed on a module's input that decides whether each incoming bundle is allowed to proceed into the module or is blocked. Filters are the primary mechanism for controlling data flow in Make scenarios.
When to Use It
- You need a simple yes/no gate: "Only process bundles where field X meets condition Y."
- You want to control which bundles take which route after a Router — see Routing.
- You want to skip processing for certain data without splitting the flow.
How It Works in Make
1. Every module (except If-Else, Merge, and error handlers) can have an input filter. 2. The filter sits on the module's input — before the module executes. 3. For each incoming bundle, the filter evaluates its conditions:
- Pass: The bundle enters the module and gets processed.
- Block: The bundle is stopped. The module does not execute for that bundle. All downstream modules in that path also skip.
Available Operators
Filters support conditions using:
- Numeric operators: equal to, not equal to, greater than, greater than or equal to, less than, less than or equal to
- Text operators: equal to, not equal to, contains, does not contain, starts with, ends with, matches pattern (regex)
- Date operators: before, after, between
- Existence operators: exists, does not exist
- Array operators: contains
Compound Conditions (AND/OR)
A single filter can combine multiple rules:
- AND: All rules must match for the bundle to pass. Add multiple rules within the same condition group.
- OR: Any rule matching is sufficient. Add a new condition group (each group is OR'd together, rules within a group are AND'd).
Filters, Router Conditions, and If-Else Conditions
These all use the same condition mechanism (same operators, same AND/OR composition). The difference is where they're applied:
- Filter: on any module input — pass/block gate on a single path
- Router condition: on each route — determines which routes fire (multiple can match)
- If-Else condition: on each branch — determines which single branch executes (first match wins)
Filter configuration details are handled in later phases — during Phase 1, just note where filters will be needed.
Filter vs Router Decision
Use a Filter when:
- Simple yes/no gate on a single path: "Only send Slack if priority = High"
- One execution path — bundles either proceed or stop
- Example:
Get Rows → Filter (status = 'New') → Send Slack Message
Use a Router when:
- Multiple distinct paths with different actions per condition
- Different conditions require different module sequences
- Example: "If status = 'New', do A. If status = 'Done', do B. If status = 'Error', do C."
Common mistake: Using a Router for a simple yes/no check. If there's only one path and you just want to skip bundles that don't match, a Filter is simpler and correct.
Flowchart Notation
Google Sheets - Search Rows → [filter: status = "active"] → Slack - Send MessageWith Router:
Trigger → Router
├─ Route 1 [filter: status = "new"]: Slack - Send Message
├─ Route 2 [filter: status = "done"]: Archive Module
└─ Route 3 [filter: status = "error"]: Email - Send AlertExample
Only process orders above $100:
Shopify - Watch Orders → [filter: amount > 100] → Slack - Notify Team → Google Sheets - Log OrderOrders at or below $100 are silently dropped at the filter — Slack and Google Sheets never execute for them.
Gotchas
- Downstream cascade. If a filter blocks a bundle, ALL downstream modules in that path skip for that bundle — not just the module with the filter.
- Not on If-Else/Merge. These modules handle conditions differently (built-in to their logic). You don't add input filters to them.
- Invisible failures. Unlike errors, filtered bundles produce no output or notification. If your scenario seems to "do nothing," check if a filter is blocking all bundles.
Official Documentation
See also: Routing for splitting into multiple conditional paths, Branching for mutually exclusive logic.
Merging
What It Is
The Merge module converges branches created by the If-Else module back into a single flow. After the merge point, the scenario continues as a linear sequence regardless of which branch was taken.
When to Use It
- You used If-Else branching and need the scenario to continue with common steps after the conditional logic.
- Example: Different processing per ticket type, but all tickets get a confirmation email afterward.
How It Works in Make
1. Create an If-Else branching structure — see Branching. 2. Place a Merge module after the branches. 3. All branches connect into the Merge module. 4. The output bundle from whichever branch executed flows through the Merge and into subsequent modules.
Compatibility
- Works with: If-Else module branches only.
- Does NOT work with: Router routes. Router routes cannot be merged — they remain independent forks permanently.
Blueprint Structure
In the blueprint JSON, builtin:BasicMerge sits in the top-level flow array immediately after builtin:BasicIfElse. It receives the output from whichever branch executed.
{
"id": 8,
"module": "builtin:BasicMerge",
"version": 1,
"mapper": null,
"metadata": { "designer": { "x": 900, "y": 150 } },
"filters": [null, null],
"outputs": []
}Key fields:
- `filters` — Array with one entry per branch on the preceding If-Else module. Use
nullfor no filter (pass-through). To conditionally filter a branch's output at merge time, replacenullwith a filter object. - `outputs` — Output mappings array. Use
[]for simple pass-through (the bundle from the branch flows through unchanged). - `mapper` — Always
nullfor BasicMerge.
The number of entries in filters must match the number of branches on the If-Else module. For example, an If-Else with 3 branches (two conditions + else) requires "filters": [null, null, null].
Flowchart Notation
If-Else
├─ If (condition A): Module X
└─ Else: Module Y
→ Merge → Module Z (runs regardless of which branch was taken)Example
Webhook → If-Else
├─ If (source = "Shopify"): Transform Shopify Data
└─ Else: Transform Generic Data
→ Merge → CRM - Create Contact → Email - Send WelcomeBoth branches produce a normalized contact bundle; after Merge, the same CRM and Email modules process it.
Gotchas
- Router routes cannot merge. This is the most common mistake. If you need convergence, use If-Else + Merge, not Router. For Router workarounds, see the Converger concept in Routing.
- Bundle continuity. The bundle that exits Merge is the output of whichever branch executed. Ensure both branches produce compatible data for downstream modules.
- Credits. The Merge module uses operations but does not consume credits.
Official Documentation
See also: Branching for If-Else usage, Routing for non-mergeable parallel routes.