{
  "id": "GE4ttRmIwrgfsKRj",
  "name": "mercadopago-bulk-payment-links-template",
  "tags": [],
  "nodes": [
    {
      "id": "1a244fc0-a7cd-459d-9a87-e7f709f1887d",
      "name": "About this template",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -464,
        432
      ],
      "parameters": {
        "color": 4,
        "width": 3200,
        "height": 780,
        "content": "## Who is this for\n\nBusinesses and freelancers using MercadoPago who need to send payment links to many customers at once \u2014 courses, subscriptions, invoices, or any product billed individually.\n\n## How it works\n\nReads a Google Sheet row by row, generates a unique MercadoPago Checkout Link for each row, writes the link back to the sheet, then delivers it through the channel the customer prefers: **Email** (Gmail), **WhatsApp Business**, or **Telegram**. A single batch can mix delivery methods \u2014 each row picks its own channel.\n\n## How to set up\n\n1. **Create a Google Sheet** with the required columns (see the *Step 1* note below).\n2. Open the **Read Sheet** node and select your document and sheet.\n3. Connect your **MercadoPago API** credential on the *Create Payment Link* node.\n4. Connect your **Google Sheets OAuth** credential on all three Sheets nodes.\n5. Choose your delivery channel(s), connect credentials, and **disable the branches you don't use** (right-click \u2192 Disable).\n6. Click **Execute Workflow**. Re-runs are safe \u2014 rows with an existing link are skipped automatically.\n\n## Requirements\n\n- MercadoPago account with a valid Access Token \u2192 [Get yours here](https://www.mercadopago.com.ar/developers/es/docs/credentials)\n- Google account with Sheets access\n- At least one of: Gmail / WhatsApp Business Cloud / Telegram bot\n\n## How to customize\n\n- Replace **Gmail** with the *Send Email (SMTP)* node for custom mail servers.\n- Replace the **Manual Trigger** with a **Schedule Trigger** to run automatically on a cron.\n- Add an **Error Trigger** branch to get notified on failures.\n\n---\n\n> \u26a0\ufe0f **Community node notice:** This template uses `@mercadopago/n8n-nodes-mercadopago` and works on **self-hosted n8n only**. It will not run on n8n Cloud unless the node is installed by your instance admin."
      },
      "typeVersion": 1
    },
    {
      "id": "39fa508f-0660-4e1a-8c0a-5f0929ab5e59",
      "name": "Step 1 \u2014 Google Sheet",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1136,
        1264
      ],
      "parameters": {
        "width": 1092,
        "height": 436,
        "content": "## Step 1 \u2014 Prepare your Google Sheet\n\nCreate a new sheet and paste these headers into **row 1**:\n\n```\ncustomer_name | customer_email | customer_phone | customer_telegram_chat_id | product_id | product_title | product_description | quantity | unit_price | currency_id | external_reference | channel\n```\n\nLeave these output columns blank \u2014 the workflow fills them:\n\n```\npayment_link | preference_id | link_created_at | status | sent_at | channel_used\n```\n\n**Required per row:** `product_title`, `quantity` (\u2265 1), `unit_price` (> 0), a unique `external_reference` (max 64 chars), `channel` (`email` / `whatsapp` / `telegram`), and the contact field matching the channel.\n\nThen open **Read Sheet** and select your document and sheet tab."
      },
      "typeVersion": 1
    },
    {
      "id": "45eaddc6-4fe4-4f75-9dbe-735b35fb0930",
      "name": "Step 2 \u2014 Generate link",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        32,
        1280
      ],
      "parameters": {
        "width": 728,
        "height": 236,
        "content": "## Step 2 \u2014 Generate and save the payment link\n\nFor each row that doesn't yet have a `payment_link`, the **Create Payment Link** node calls MercadoPago's Checkout Preferences API and returns a unique `init_point` URL.\n\nThe link is immediately written back to the sheet before delivery \u2014 so even if the notification fails, the link is always persisted and can be resent manually."
      },
      "typeVersion": 1
    },
    {
      "id": "1b0cf5db-88a3-4580-9968-549d553f2240",
      "name": "Step 3 \u2014 Route and deliver",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        864,
        1264
      ],
      "parameters": {
        "width": 1380,
        "height": 280,
        "content": "## Step 3 \u2014 Deliver to the customer\n\nThe **Route by channel** node reads the `channel` column of each row and forwards it to the correct branch:\n\n- `email` \u2192 Gmail sends an HTML email with the payment link\n- `whatsapp` \u2192 WhatsApp Business Cloud sends a text message\n- `telegram` \u2192 Telegram bot sends a message\n\nAfter delivery, the sheet is updated with `status = sent`, `sent_at`, and `channel_used`.\n\n**Disable branches you don't need** \u2014 right-click any node \u2192 Disable."
      },
      "typeVersion": 1
    },
    {
      "id": "d8390986-da6a-4d8a-9423-b202fd9da9c8",
      "name": "Schedule \u2014 optional",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -464,
        1952
      ],
      "parameters": {
        "width": 400,
        "height": 268,
        "content": "## \u23f1 Optional \u2014 Schedule automatic runs\n\n1. Delete the **Manual Trigger** node.\n2. Add a **Schedule Trigger** in its place and connect it to **Read Sheet**.\n3. Set the interval (e.g. every hour, every morning).\n\nThe *Skip already processed* filter ensures re-runs never regenerate existing links."
      },
      "typeVersion": 1
    },
    {
      "id": "82e8cfa4-83ef-4dd9-b3a2-af0ca9b0aff5",
      "name": "When clicking 'Execute Workflow'",
      "type": "n8n-nodes-base.manualTrigger",
      "position": [
        -464,
        1760
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "081224c1-0c00-4327-b55b-40b7edd1ea84",
      "name": "Read Sheet",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -224,
        1760
      ],
      "parameters": {
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "",
          "cachedResultUrl": "",
          "cachedResultName": "Sheet1"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "",
          "cachedResultUrl": "",
          "cachedResultName": "Select your Google Sheet"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "0acb70da-43da-47fc-897f-8caf355cde69",
      "name": "Skip already processed",
      "type": "n8n-nodes-base.if",
      "position": [
        16,
        1760
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "cond-link-empty",
              "operator": {
                "type": "string",
                "operation": "empty",
                "singleValue": true
              },
              "leftValue": "={{ $json.payment_link }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "8f0ec4e8-45cd-4a46-b942-594e675f378b",
      "name": "Create Payment Link",
      "type": "@mercadopago/n8n-nodes-mercadopago.mercadoPago",
      "position": [
        256,
        1760
      ],
      "parameters": {
        "items": {
          "itemsValues": [
            {
              "id": "={{ $json.product_id }}",
              "title": "={{ $json.product_title }}",
              "quantity": "={{ Number($json.quantity) }}",
              "unit_price": "={{ Number($json.unit_price) }}",
              "currency_id": "={{ $json.currency_id }}",
              "description": "={{ $json.product_description }}"
            }
          ]
        },
        "additionalFields": {
          "external_reference": "={{ $json.external_reference }}"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "103b739e-ddc8-4f0d-bdf7-886cf2cfecdd",
      "name": "Update Sheet \u2014 Link",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        496,
        1760
      ],
      "parameters": {
        "columns": {
          "value": {
            "payment_link": "={{ $json.init_point }}",
            "preference_id": "={{ $json.id }}",
            "link_created_at": "={{ $now.toISO() }}",
            "external_reference": "={{ $('Skip already processed').item.json.external_reference }}"
          },
          "schema": [],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "external_reference"
          ]
        },
        "options": {},
        "operation": "appendOrUpdate",
        "sheetName": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('Read Sheet').params.sheetName.value }}"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('Read Sheet').params.documentId.value }}"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "9c32bb30-32c3-4ab2-8435-443b57ee399a",
      "name": "Route by channel",
      "type": "n8n-nodes-base.switch",
      "position": [
        736,
        1760
      ],
      "parameters": {
        "rules": {
          "values": [
            {
              "outputKey": "email",
              "conditions": {
                "options": {
                  "leftValue": "",
                  "caseSensitive": false,
                  "typeValidation": "loose"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "rule-email",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $('Skip already processed').item.json.channel }}",
                    "rightValue": "email"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "whatsapp",
              "conditions": {
                "options": {
                  "leftValue": "",
                  "caseSensitive": false,
                  "typeValidation": "loose"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "rule-whatsapp",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $('Skip already processed').item.json.channel }}",
                    "rightValue": "whatsapp"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "telegram",
              "conditions": {
                "options": {
                  "leftValue": "",
                  "caseSensitive": false,
                  "typeValidation": "loose"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "rule-telegram",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $('Skip already processed').item.json.channel }}",
                    "rightValue": "telegram"
                  }
                ]
              },
              "renameOutput": true
            }
          ]
        },
        "options": {}
      },
      "typeVersion": 3.2
    },
    {
      "id": "8a96199d-b702-4412-9dcf-49a87c0fb77a",
      "name": "Email \u2014 setup",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1488,
        1568
      ],
      "parameters": {
        "width": 660,
        "height": 240,
        "content": "## \ud83d\udce7 Email \u2014 setup\n\n1. In n8n go to **Credentials \u2192 New \u2192 Gmail OAuth2 API** and follow the OAuth flow.\n2. Assign the credential to the **Send Email** node.\n3. Make sure each row has `customer_email` filled and `channel = email`.\n\nTo use SMTP instead, replace the Gmail node with **Send Email (SMTP)** and configure host / port / user / password."
      },
      "typeVersion": 1
    },
    {
      "id": "90af133f-5f08-4f6c-8862-41328f0fb9a6",
      "name": "Send Email",
      "type": "n8n-nodes-base.gmail",
      "position": [
        976,
        1632
      ],
      "parameters": {
        "sendTo": "={{ $('Skip already processed').item.json.customer_email }}",
        "message": "=<p>Hi <b>{{ $('Skip already processed').item.json.customer_name }}</b>,</p>\n<p>Your secure payment link for <b>{{ $('Skip already processed').item.json.product_title }}</b> is ready:</p>\n<p><a href=\"{{ $('Update Sheet \u2014 Link').item.json.payment_link }}\">Pay now with MercadoPago \u2192</a></p>\n<p>Amount: <b>{{ $('Skip already processed').item.json.currency_id }} {{ $('Skip already processed').item.json.unit_price }}</b><br>Reference: {{ $('Skip already processed').item.json.external_reference }}</p>\n<p>Thank you!</p>",
        "options": {},
        "subject": "=Your payment link \u2014 {{ $('Skip already processed').item.json.product_title }}"
      },
      "typeVersion": 2.1
    },
    {
      "id": "a3b8df88-d6e3-4e9d-987d-7df7fd5dba72",
      "name": "WhatsApp \u2014 setup",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1488,
        1856
      ],
      "parameters": {
        "width": 660,
        "height": 280,
        "content": "## \ud83d\udcf1 WhatsApp \u2014 setup\n\n1. Create a Meta Business app and get your **permanent access token** and **business account ID**: [Meta for Developers guide](https://developers.facebook.com/docs/whatsapp/cloud-api/get-started)\n2. In n8n go to **Credentials \u2192 New \u2192 WhatsApp Business Cloud API** and paste both values.\n3. Assign the credential to **Send WhatsApp** and select your phone number from the dropdown.\n4. In sandbox: add the recipient to your test numbers. In production: make sure a 24h customer window is open or use an approved template.\n\nRows must have `customer_phone` as digits only with country code \u2014 no `+` prefix (e.g. `5491123456789`)."
      },
      "typeVersion": 1
    },
    {
      "id": "a90c4b8c-02fe-435d-b6ee-55d48b1e5f7c",
      "name": "Send WhatsApp",
      "type": "n8n-nodes-base.whatsApp",
      "position": [
        976,
        2032
      ],
      "parameters": {
        "textBody": "=Hi {{ $('Skip already processed').item.json.customer_name }}, your payment link for {{ $('Skip already processed').item.json.product_title }}:\n\n{{ $('Update Sheet \u2014 Link').item.json.payment_link }}\n\nAmount: {{ $('Skip already processed').item.json.currency_id }} {{ $('Skip already processed').item.json.unit_price }}\nRef: {{ $('Skip already processed').item.json.external_reference }}",
        "operation": "send",
        "phoneNumberId": {
          "__rl": true,
          "mode": "list",
          "value": "",
          "cachedResultName": "Select your phone number"
        },
        "additionalFields": {
          "previewUrl": true
        },
        "recipientPhoneNumber": "={{ $('Skip already processed').item.json.customer_phone.replace(/^\\+/, '') }}"
      },
      "typeVersion": 1
    },
    {
      "id": "a8714432-f9d8-431d-a004-bbf3c4353d75",
      "name": "Telegram \u2014 setup",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1488,
        2304
      ],
      "parameters": {
        "width": 660,
        "height": 260,
        "content": "## \ud83d\udce8 Telegram \u2014 setup\n\n1. Open Telegram and talk to **@BotFather** \u2192 `/newbot` \u2192 copy the bot token.\n2. In n8n go to **Credentials \u2192 New \u2192 Telegram API** and paste the token.\n3. Each customer must **send `/start`** to your bot at least once before they can receive messages.\n4. Use **@userinfobot** to get each customer's numeric `chat_id`.\n5. Fill the `customer_telegram_chat_id` column in your sheet.\n\nRows must have `customer_telegram_chat_id` filled and `channel = telegram`."
      },
      "typeVersion": 1
    },
    {
      "id": "d4bcc843-3295-4dc4-8918-d215c70d11e5",
      "name": "Send Telegram",
      "type": "n8n-nodes-base.telegram",
      "position": [
        976,
        2432
      ],
      "parameters": {
        "text": "=Hi {{ $('Skip already processed').item.json.customer_name }}, your payment link for {{ $('Skip already processed').item.json.product_title }}:\n\n{{ $('Update Sheet \u2014 Link').item.json.payment_link }}\n\nAmount: {{ $('Skip already processed').item.json.currency_id }} {{ $('Skip already processed').item.json.unit_price }}\nRef: {{ $('Skip already processed').item.json.external_reference }}",
        "chatId": "={{ $('Skip already processed').item.json.customer_telegram_chat_id }}",
        "additionalFields": {}
      },
      "typeVersion": 1.2
    },
    {
      "id": "efa6ac59-9810-4fc5-8aba-61807b47c4b6",
      "name": "Mark Sent \u2014 Email",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1216,
        1632
      ],
      "parameters": {
        "columns": {
          "value": {
            "status": "sent",
            "sent_at": "={{ $now.toISO() }}",
            "channel_used": "email",
            "external_reference": "={{ $('Skip already processed').item.json.external_reference }}"
          },
          "schema": [],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "external_reference"
          ]
        },
        "options": {},
        "operation": "appendOrUpdate",
        "sheetName": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('Read Sheet').params.sheetName.value }}"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('Read Sheet').params.documentId.value }}"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "894daaef-99f0-42d5-b91d-f7a6a0750519",
      "name": "Mark Sent \u2014 WhatsApp",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1216,
        2032
      ],
      "parameters": {
        "columns": {
          "value": {
            "status": "sent",
            "sent_at": "={{ $now.toISO() }}",
            "channel_used": "whatsapp",
            "external_reference": "={{ $('Skip already processed').item.json.external_reference }}"
          },
          "schema": [],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "external_reference"
          ]
        },
        "options": {},
        "operation": "appendOrUpdate",
        "sheetName": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('Read Sheet').params.sheetName.value }}"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('Read Sheet').params.documentId.value }}"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "57279ec9-bed5-447c-838c-addcc5d2b797",
      "name": "Mark Sent \u2014 Telegram",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1216,
        2432
      ],
      "parameters": {
        "columns": {
          "value": {
            "status": "sent",
            "sent_at": "={{ $now.toISO() }}",
            "channel_used": "telegram",
            "external_reference": "={{ $('Skip already processed').item.json.external_reference }}"
          },
          "schema": [],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "external_reference"
          ]
        },
        "options": {},
        "operation": "appendOrUpdate",
        "sheetName": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('Read Sheet').params.sheetName.value }}"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('Read Sheet').params.documentId.value }}"
        }
      },
      "typeVersion": 4.5
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "24522ecf-218b-4862-be52-9aa8d39ce28f",
  "connections": {
    "Read Sheet": {
      "main": [
        [
          {
            "node": "Skip already processed",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send Email": {
      "main": [
        [
          {
            "node": "Mark Sent \u2014 Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send Telegram": {
      "main": [
        [
          {
            "node": "Mark Sent \u2014 Telegram",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send WhatsApp": {
      "main": [
        [
          {
            "node": "Mark Sent \u2014 WhatsApp",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Route by channel": {
      "main": [
        [
          {
            "node": "Send Email",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Send WhatsApp",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Send Telegram",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create Payment Link": {
      "main": [
        [
          {
            "node": "Update Sheet \u2014 Link",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Update Sheet \u2014 Link": {
      "main": [
        [
          {
            "node": "Route by channel",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Skip already processed": {
      "main": [
        [
          {
            "node": "Create Payment Link",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "When clicking 'Execute Workflow'": {
      "main": [
        [
          {
            "node": "Read Sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}