{
  "id": "1l9SEOqC8LDKXgAM",
  "name": "Generate and validate QR codes in bulk with Google Sheets and Google Drive",
  "tags": [],
  "nodes": [
    {
      "id": "adfa1fec-69d0-499d-a544-039ff540748a",
      "name": "When clicking \u2018Execute workflow\u2019",
      "type": "n8n-nodes-base.manualTrigger",
      "position": [
        -1632,
        136
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "fa465b9d-f404-4ece-8dbf-3eb736354aca",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1408,
        560
      ],
      "parameters": {
        "color": 7,
        "width": 360,
        "height": 220,
        "content": "Validate whether the scanned QR code is valid"
      },
      "typeVersion": 1
    },
    {
      "id": "a80a4648-0e59-4728-82c7-ffeec8106288",
      "name": "Not used?",
      "type": "n8n-nodes-base.if",
      "position": [
        -720,
        512
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "50546d87-ab37-4c8a-b835-e1a60f3aa0dc",
              "operator": {
                "type": "string",
                "operation": "empty",
                "singleValue": true
              },
              "leftValue": "={{ $json['status_qr'] }}",
              "rightValue": ""
            },
            {
              "id": "025a42b8-fb90-44a8-951a-0e321b42c3b9",
              "operator": {
                "type": "number",
                "operation": "exists",
                "singleValue": true
              },
              "leftValue": "={{ $json.row_number }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "1741c140-8470-4a84-bced-1710f2a11b2a",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -960,
        464
      ],
      "parameters": {
        "color": 7,
        "width": 360,
        "height": 220,
        "content": "Check if ID is valid"
      },
      "typeVersion": 1
    },
    {
      "id": "e7b9c024-bcb9-488c-a034-b3bf04546f35",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -544,
        368
      ],
      "parameters": {
        "color": 7,
        "width": 420,
        "height": 220,
        "content": "Update the QR status after it has been used"
      },
      "typeVersion": 1
    },
    {
      "id": "8ad38195-9077-420e-b724-f18bc8e5c04d",
      "name": "Upload file",
      "type": "n8n-nodes-base.googleDrive",
      "position": [
        -512,
        64
      ],
      "parameters": {
        "name": "={{ $('Loop Over Items').item.json.email }}",
        "driveId": {
          "__rl": true,
          "mode": "list",
          "value": "My Drive"
        },
        "options": {},
        "folderId": {
          "__rl": true,
          "mode": "list",
          "value": ""
        }
      },
      "typeVersion": 3
    },
    {
      "id": "9a2a2d6c-b69f-4693-bc32-e65617391bed",
      "name": "Loop Over Items",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        -992,
        48
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 3
    },
    {
      "id": "23096106-7fe7-478e-8ded-9115a3a9bbf0",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1428,
        76
      ],
      "parameters": {
        "color": 7,
        "width": 360,
        "height": 220,
        "content": "Check whether a QR code has already been generated for this ID"
      },
      "typeVersion": 1
    },
    {
      "id": "ee444bab-8a5c-4aea-a11c-771f984af2b7",
      "name": "Sticky Note6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -834,
        4
      ],
      "parameters": {
        "color": 7,
        "width": 516,
        "height": 220,
        "content": "Generate a QR code using the webhook validation URL and the unique ID"
      },
      "typeVersion": 1
    },
    {
      "id": "35a79222-3544-4fb7-bcb6-fa80dd2cf450",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -288,
        0
      ],
      "parameters": {
        "color": 7,
        "width": 424,
        "height": 220,
        "content": "Update the status of the ID in Google Sheets"
      },
      "typeVersion": 1
    },
    {
      "id": "4a979c76-2e9d-4fc6-a649-63c22b4139e3",
      "name": "Not Valid",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        -944,
        704
      ],
      "parameters": {
        "options": {
          "responseCode": 200
        },
        "respondWith": "text",
        "responseBody": "QR not valid"
      },
      "typeVersion": 1.1
    },
    {
      "id": "b4bdf87c-a33c-4966-b10f-e1652d1a46fc",
      "name": "QR Generator",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -736,
        64
      ],
      "parameters": {
        "url": "=https://api.qrserver.com/v1/create-qr-code/?size=300x300&data={{ $env.WEBHOOK_URL }}/webhook/read-qr?id={{$json.email}}",
        "options": {}
      },
      "typeVersion": 4.2
    },
    {
      "id": "480aa947-8531-4087-92a7-83cef2f04658",
      "name": "Update QR used",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -496,
        416
      ],
      "parameters": {
        "operation": "update",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "",
          "cachedResultUrl": "",
          "cachedResultName": "list_user"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": ""
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "77d384fa-6330-4f71-a25f-b649e4ef2b81",
      "name": "QR ID Already Used",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        -496,
        608
      ],
      "parameters": {
        "options": {
          "responseCode": 200
        },
        "respondWith": "text",
        "responseBody": "QR ID Already Used"
      },
      "typeVersion": 1.1
    },
    {
      "id": "01bf254f-ab08-4b90-94c0-707f27a0a459",
      "name": "QR OK",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        -272,
        416
      ],
      "parameters": {
        "options": {},
        "respondWith": "text",
        "responseBody": "QR Valid!"
      },
      "typeVersion": 1.1
    },
    {
      "id": "ec24abba-fc81-4c35-bcbe-2286dc554566",
      "name": "Get List",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -1408,
        136
      ],
      "parameters": {
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "",
          "cachedResultUrl": "",
          "cachedResultName": ""
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": ""
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "7e4479a7-47a7-4f19-9d37-601e82d68893",
      "name": "Get List ID",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -944,
        512
      ],
      "parameters": {
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "",
          "cachedResultUrl": "",
          "cachedResultName": "list_user"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": ""
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "7059f2ea-1106-46b7-930a-b26a0a8a55e6",
      "name": "Check is generated?",
      "type": "n8n-nodes-base.if",
      "position": [
        -1184,
        136
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "5a81e1a4-e6c9-4b00-b5d7-46021dffa930",
              "operator": {
                "type": "string",
                "operation": "notEquals"
              },
              "leftValue": "={{ $json.status_qr }}",
              "rightValue": "QR_GENERATED"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "4b9f83f3-673c-463c-9f39-4a8c87ceecbd",
      "name": "Update Row Status QR Generated",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -32,
        64
      ],
      "parameters": {
        "operation": "update",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "",
          "cachedResultUrl": "",
          "cachedResultName": "list_user"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": ""
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "7941fec2-ad64-44aa-92d6-b11286913eae",
      "name": "Check ID Exists",
      "type": "n8n-nodes-base.if",
      "position": [
        -1168,
        608
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "430aee2d-a788-4d7c-ab64-880c900f0058",
              "operator": {
                "type": "string",
                "operation": "exists",
                "singleValue": true
              },
              "leftValue": "={{ $json.id }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "f3806868-fc58-4ef1-b43b-2eb126dd12a1",
      "name": "QR Validation Webhook",
      "type": "n8n-nodes-base.webhook",
      "position": [
        -1616,
        608
      ],
      "parameters": {
        "path": "read-qr",
        "options": {},
        "responseMode": "responseNode"
      },
      "typeVersion": 2
    },
    {
      "id": "faa5b030-7b26-4af8-a367-4fdfa5f1c4b5",
      "name": "Extract ID from Webhook",
      "type": "n8n-nodes-base.set",
      "position": [
        -1392,
        608
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "4e9a4330-d654-410f-9b99-aa57545c2c80",
              "name": "id",
              "type": "string",
              "value": "={{ $json.query.id }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "64a9eed4-b306-4def-9367-a6bd3292f6a2",
      "name": "Prepare Status Update",
      "type": "n8n-nodes-base.set",
      "position": [
        -256,
        64
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "373e8180-bad8-49cc-a060-1e4a1c055384",
              "name": "id",
              "type": "string",
              "value": "={{ $('Loop Over Items').item.json.email }}"
            },
            {
              "id": "6c7206e9-500e-46ce-bf3f-fb062b59b293",
              "name": "status",
              "type": "string",
              "value": "QR_GENERATED"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "9d5e4d7a-c3e5-4c7c-9afd-6bbbf50147e7",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2288,
        -80
      ],
      "parameters": {
        "width": 576,
        "height": 944,
        "content": "## Generate and validate QR codes in bulk with Google Sheets and Google Drive\nThis workflow allows you to generate QR codes (Barcodes) in bulk from a Google Sheets file and store the generated QR images automatically in Google Drive. Each QR code contains a unique identifier (in this template, an email address) and is connected to a validation webhook.\n\nThe workflow is designed to be modular, easy to customize, and suitable for real-world implementation.\n\n\n## How it works\nThe workflow has two parts:\n\n### 1. QR Generation\n- Reads rows from Google Sheets\n- Checks whether a QR code has already been generated\n- Creates a QR code using a webhook validation URL\n- Uploads the QR image to Google Drive\n- Updates the status column to \"QR_GENERATED\"\n\n### 2. QR Validation\n- Triggered when a QR code is scanned\n- Extracts the ID from the URL\n- Verifies whether the ID exists\n- Checks whether it has already been used\n- Updates the status to \"QR_USED\"\n- Returns a validation response\n\n## Setup steps\n1. Connect your Google Sheets and Google Drive credentials.\n2. Select your spreadsheet and target Drive folder. Ensure your sheet contains email & status_qr columns.\n3. Replace the webhook base URL in the QR generator node with your production URL.\n4. Activate the workflow.\n\n### Need Help?\nContact me on [LinkedIn](https://www.linkedin.com/in/dwicahyas/)!\n"
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "b5d92c50-cfba-4c5a-966e-0d0cf395b7b4",
  "connections": {
    "Get List": {
      "main": [
        [
          {
            "node": "Check is generated?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Not used?": {
      "main": [
        [
          {
            "node": "Update QR used",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "QR ID Already Used",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get List ID": {
      "main": [
        [
          {
            "node": "Not used?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Upload file": {
      "main": [
        [
          {
            "node": "Prepare Status Update",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "QR Generator": {
      "main": [
        [
          {
            "node": "Upload file",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Update QR used": {
      "main": [
        [
          {
            "node": "QR OK",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check ID Exists": {
      "main": [
        [
          {
            "node": "Get List ID",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Not Valid",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Loop Over Items": {
      "main": [
        [],
        [
          {
            "node": "QR Generator",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check is generated?": {
      "main": [
        [
          {
            "node": "Loop Over Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Status Update": {
      "main": [
        [
          {
            "node": "Update Row Status QR Generated",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "QR Validation Webhook": {
      "main": [
        [
          {
            "node": "Extract ID from Webhook",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract ID from Webhook": {
      "main": [
        [
          {
            "node": "Check ID Exists",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Update Row Status QR Generated": {
      "main": [
        [
          {
            "node": "Loop Over Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "When clicking \u2018Execute workflow\u2019": {
      "main": [
        [
          {
            "node": "Get List",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}