Make.com Data Stores are lightweight key-value databases built into the platform. Many scenarios in this library use data stores to track state between runs — abandoned cart status, drip campaign progress, approval records, loyalty points, and more.
This guide covers creation, schema design, and common patterns.
Make.com scenarios are stateless by default — each run processes new data without remembering what happened in previous runs. Data stores add memory:
1. In Make.com, go to Data stores in the left sidebar
2. Click Add data store
3. Enter the name — use the exact name referenced in the scenario (e.g., abandoned_cart_tracking)
4. Set the data structure — see schema definitions below
5. Set the data store size — the free tier includes 1 MB storage; most tracking use cases fit easily
When you create a data store, you define its fields (called a "data structure"):
1. Click Add to create a new data structure
2. Name it (e.g., "Abandoned Cart Schema")
3. Click Generator and paste a sample JSON object to auto-detect fields, OR manually add fields:
checkout_id)Name: abandoned_cart_tracking
| Field | Type | Required | Notes |
|---|---|---|---|
checkout_id | Number | Yes | Used as the record key |
email | Text | Yes | Customer's email address |
cart_value | Number | No | Total cart value in dollars |
first_email_sent | Boolean | Yes | Default: false |
second_email_sent | Boolean | Yes | Default: false |
first_sent_at | Date | No | When the first reminder was sent |
second_sent_at | Date | No | When the discount follow-up was sent |
discount_code | Text | No | Generated discount code, if applicable |
Make.com provides these modules for working with data stores:
Finds records matching filter criteria. Returns an array of matching records.
{
"module": "datastore:search-records",
"parameters": {
"data_store": "store_name"
},
"mapper": {
"filter": [
{ "key": "field_name", "value": "match_value", "operator": "equal" }
],
"limit": 50
}
}Operators: equal, notequal, greater, less, greaterorequal, lessorequal, contain, notcontain
Output fields:
__KEY__ — The record's unique key (for updates/deletes)__IMTLENGTH__ — Number of matching records (useful for existence checks)Creates a new record. Fails if a record with the same key already exists.
{
"module": "datastore:add-record",
"parameters": {
"data_store": "store_name"
},
"mapper": {
"key": "unique_identifier",
"field1": "value1",
"field2": "value2"
}
}Updates an existing record by key.
{
"module": "datastore:update-record",
"parameters": {
"data_store": "store_name"
},
"mapper": {
"key": "{{previous_module.__KEY__}}",
"field_to_update": "new_value"
}
}Creates a new record OR updates an existing one. Combines add + update into one operation.
{
"module": "datastore:upsert-record",
"parameters": {
"data_store": "store_name"
},
"mapper": {
"key": "unique_identifier",
"field1": "value1",
"field2": "value2"
}
}Removes a record by key.
{
"module": "datastore:delete-record",
"parameters": {
"data_store": "store_name"
},
"mapper": {
"key": "{{previous_module.__KEY__}}"
}
}These scenarios reference specific data stores. Create each one before using the associated scenarios.
abandoned_cart_trackingUsed by: ecommerce/ecommerce-orders-fulfillment.json (Abandoned Cart Recovery)
| Field | Type | Key? |
|---|---|---|
| checkout_id | Number | Yes |
| Text | ||
| cart_value | Number | |
| first_email_sent | Boolean | |
| second_email_sent | Boolean | |
| first_sent_at | Date | |
| second_sent_at | Date | |
| discount_code | Text |
drip_campaign_contactsUsed by: marketing/marketing-campaign-automation.json (Drip Email Sequence)
| Field | Type | Key? |
|---|---|---|
| Text | Yes | |
| first_name | Text | |
| current_step | Number | |
| next_send_date | Date | |
| status | Text | |
| enrolled_at | Date | |
| last_sent_at | Date | |
| completed_at | Date |
newsletter_subscribersUsed by: marketing/marketing-lead-capture.json (Newsletter Double Opt-In)
| Field | Type | Key? |
|---|---|---|
| Text | Yes | |
| first_name | Text | |
| source_page | Text | |
| confirmation_token | Text | |
| status | Text | |
| signed_up_at | Date | |
| confirmed_at | Date |
review_request_queueUsed by: ecommerce/review-request-sequence.json
| Field | Type | Key? |
|---|---|---|
| order_id | Text | Yes |
| customer_email | Text | |
| customer_name | Text | |
| product_name | Text | |
| product_handle | Text | |
| status | Text | |
| send_after | Date | |
| attempt | Number | |
| last_sent_at | Date |
loyalty_pointsUsed by: ecommerce/loyalty-points-tracker.json
| Field | Type | Key? |
|---|---|---|
| Text | Yes | |
| customer_name | Text | |
| total_points | Number | |
| tier | Text | |
| total_spent | Number | |
| order_count | Number | |
| last_order_at | Date |
invoice_remindersUsed by: finance/overdue-invoice-reminders.json
| Field | Type | Key? |
|---|---|---|
| invoice_id | Text | Yes |
| customer_email | Text | |
| amount_due | Number | |
| last_level_sent | Text | |
| last_sent_at | Date | |
| days_overdue_at_last_send | Number |
approval_requestsUsed by: operations/approval-workflow.json
| Field | Type | Key? |
|---|---|---|
| request_id | Text | Yes |
| requester_name | Text | |
| requester_email | Text | |
| requester_slack_id | Text | |
| request_type | Text | |
| description | Text | |
| amount | Number | |
| approval_level | Text | |
| approver_slack_id | Text | |
| status | Text | |
| submitted_at | Date | |
| sla_deadline | Date | |
| decided_at | Date | |
| decision_note | Text |
standup_trackingUsed by: operations/standup-collector.json
| Field | Type | Key? |
|---|---|---|
| key | Text | Yes |
| name | Text | |
| slack_user_id | Text | |
| date | Date | |
| prompted | Boolean | |
| responded | Boolean | |
| response | Text |
incident_trackerUsed by: operations/incident-response.json
| Field | Type | Key? |
|---|---|---|
| incident_id | Text | Yes |
| title | Text | |
| severity | Text | |
| service | Text | |
| source | Text | |
| status | Text | |
| assigned_to | Text | |
| created_at | Date | |
| resolved_at | Date | |
| resolution_notes | Text |
referral_programUsed by: marketing/referral-tracking.json
| Field | Type | Key? |
|---|---|---|
| referral_code | Text | Yes |
| referrer_email | Text | |
| referrer_name | Text | |
| successful_referrals | Number | |
| total_reward_earned | Number | |
| last_referral_at | Date |
winback_trackingUsed by: marketing/customer-winback.json
| Field | Type | Key? |
|---|---|---|
| key | Text | Yes |
| Text | ||
| tier_sent | Text | |
| sent_at | Date | |
| days_lapsed | Number |
The most common pattern. Search for a record, then route based on whether it exists:
Search → Router
├── Found (__IMTLENGTH__ > 0) → Update existing record
└── Not found (__IMTLENGTH__ = 0) → Create new record
Track an item through stages (e.g., pending → active → completed):
1. Search by key
2. Read current status
3. Apply transition rules
4. Update with new status
Track running totals (loyalty points, referral counts):
1. Search by key
2. Read current value
3. Calculate new value: {{current + increment}}
4. Upsert with new total
Prevent processing the same item twice:
1. Search by unique identifier
2. Filter: only continue if __IMTLENGTH__ = 0
3. Process the item
4. Add a record to mark it as processed
Rough capacity at 1 MB: ~5,000-10,000 simple records (depends on field sizes)
Performance tips:
How to make your Make.com scenarios resilient, debuggable, and production-ready using error handlers, logging, and monitoring patterns.
A scenario without error handling will stop completely when any single module fails. For a scenario processing 50 orders, if order #3 fails, orders #4-50 never get processed. Error handling lets you decide what happens on failure — retry, skip, alert, or gracefully degrade.
Make.com provides four error handler types. Each is appropriate for different situations.
When to use: Transient failures — API rate limits, network timeouts, temporary service outages.
"error_handler": {
"type": "retry",
"max_retries": 3,
"interval": 60
}| Setting | Recommended Value | Why |
|---|---|---|
max_retries | 2-5 | More than 5 usually means the issue isn't transient |
interval | 60-300 seconds | Give the external service time to recover |
Best for: Email sending, API calls to third-party services, webhook deliveries.
When to use: The module's failure is acceptable and shouldn't block the rest of the scenario.
"error_handler": {
"type": "ignore",
"message": "Optional: why this failure is acceptable"
}Best for: Non-critical notifications (Slack messages), duplicate record creation (subscriber already exists), optional enrichment steps.
Warning: Don't overuse ignore — you'll mask real problems. Always add a descriptive message so you can review ignored errors in the execution log.
When to use: The failure is important but shouldn't crash the entire scenario. The current item is skipped, but processing continues with the next item.
"error_handler": {
"type": "break",
"message": "Failed to process order {{1.id}}: {{error.message}}"
}Best for: Iterator-based flows where one bad item shouldn't stop the batch. Order processing, bulk imports, data sync operations.
When to use: When partial execution would leave data in an inconsistent state. Rolls back the entire scenario run.
"error_handler": {
"type": "rollback"
}Best for: Financial transactions, multi-step operations where all-or-nothing is required.
Module failed → Is the data critical?
├── No → IGNORE (log the skip)
└── Yes → Is it likely a transient issue?
├── Yes → RETRY (2-3 attempts, 60s intervals)
└── No → Is this inside an iterator?
├── Yes → BREAK (skip item, continue batch)
└── No → Is partial execution dangerous?
├── Yes → ROLLBACK
└── No → BREAK with alert notification
Add a Google Sheets module after your error handler to log all failures:
{
"module": "google-sheets:add-row",
"label": "Log Error",
"parameters": {
"spreadsheet_id": "YOUR_ERROR_LOG_SHEET",
"sheet_name": "Errors"
},
"mapper": {
"values": {
"A": "{{formatDate(now; 'YYYY-MM-DD HH:mm')}}",
"B": "Scenario Name",
"C": "{{error.module}}",
"D": "{{error.message}}",
"E": "{{error.status_code}}",
"F": "Item ID or context"
}
}
}Error Log sheet columns: A=Timestamp, B=Scenario, C=Failed Module, D=Error Message, E=Status Code, F=Context.
For high-priority scenarios, send a Slack alert when something fails:
{
"module": "slack:send-message",
"label": "Alert: Scenario Failure",
"parameters": { "channel": "#alerts" },
"mapper": {
"text": ":x: *Scenario Failure*\n*Scenario:* Your Scenario Name\n*Module:* {{error.module}}\n*Error:* {{error.message}}\n*Time:* {{formatDate(now; 'YYYY-MM-DD HH:mm')}}"
}
}For batch scenarios, aggregate results and send a summary:
{
"module": "builtin:set-variable",
"label": "Track Results",
"mapper": {
"variables": [
{ "name": "processed", "value": "{{counter.processed + 1}}" },
{ "name": "failed", "value": "{{counter.failed + if(error; 1; 0)}}" }
]
}
}Then after the iterator completes, send:
Processed: {{processed}} items
Failed: {{failed}} items
Success rate: {{round((processed - failed) / processed * 100)}}%
Symptom: 429 Too Many Requests or Rate limit exceeded
Solution:
1. Add a builtin:sleep module between iterations (200-500ms)
2. Reduce the schedule frequency
3. Use the retry handler with increasing intervals
{
"module": "builtin:sleep",
"parameters": { "delay": 500 },
"label": "Rate Limit Buffer (500ms)"
}Symptom: 401 Unauthorized or Token expired
Solution:
Symptom: Cannot convert value or Expected number, got text
Solution:
{{toNumber(1.amount)}}, {{toString(1.id)}}{{if(1.field; 1.field; "default")}} to handle missing fieldsSymptom: Webhook doesn't fire, or sending service reports timeout
Solution:
Create a Google Sheet with these columns to track all your scenarios:
| Column | Content |
|---|---|
| A | Date |
| B | Scenario Name |
| C | Status (Success/Partial/Failed) |
| D | Items Processed |
| E | Items Failed |
| F | Duration (seconds) |
| G | Operations Used |
Add a row to this sheet at the end of each scenario run using a Google Sheets module.
Create a dedicated "health check" scenario that runs every hour:
1. Check that all critical data stores are accessible
2. Verify each connection is still valid (make a simple API call per app)
3. Post a daily summary to Slack: "All systems operational" or list any issues
For every scenario you deploy, verify:
break handlers inside iteratorsGet the full Make Integration Library and unlock everything.
Get the complete guide with every chapter unlocked, including code samples, diagrams, and best practices.
Access all interactive tools with complete data, all workload profiles, and the full scenario library.
Downloadable source code, configuration files, and working examples from every chapter.
Free updates for life. Every new chapter, tool, and improvement included.