AutomationFlowsEmail & Gmail › Extract Attendance From Google Drive Images to Sheets with Vlm Run AI &…

Extract Attendance From Google Drive Images to Sheets with Vlm Run AI &…

Original n8n title: Extract Attendance From Google Drive Images to Sheets with Vlm Run AI & Gmail Alerts

ByShahrear @shahrear on n8n.io

This workflow automates daily attendance tracking by analyzing uploaded attendance images, extracting participant names via VLM Run’s Execute Agent, appending the structured data into Google Sheets, and emailing a formatted attendance summary through Gmail. A Google Drive…

Webhook trigger★★★☆☆ complexity11 nodes@Vlm Run/N8N Nodes VlmrunGoogle Drive TriggerGoogle DriveHTTP RequestGmail
Email & Gmail Trigger: Webhook Nodes: 11 Complexity: ★★★☆☆ Added:

This workflow corresponds to n8n.io template #9327 — we link there as the canonical source.

This workflow follows the Gmail → Google Drive recipe pattern — see all workflows that pair these two integrations.

The workflow JSON

Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →

Download .json
{
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "nodes": [
    {
      "id": "0968592f-243a-4fb9-809c-2434e9375da3",
      "name": "VLM Run for Extraction",
      "type": "@vlm-run/n8n-nodes-vlmrun.vlmRun",
      "position": [
        496,
        -96
      ],
      "parameters": {
        "operation": "executeAgent",
        "agentPrompt": "=Analyze the image of user list names and show them in the following json format, make sure to follow this exact structure, where val1 will be current date, val2 will be total user count and rest of the values will be the user names:\n{ \\\"majorDimension\\\": \\\"ROWS\\\",   \\\"values\\\": [     [\\\"val1\\\", \\\"val2\\\"]   ] }",
        "agentCallbackUrl": "https://playground.attensys.ai/webhook/check-attendance"
      },
      "credentials": {
        "vlmRunApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "32cf5900-f3f2-4a05-b3d0-97848189f513",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        864,
        -480
      ],
      "parameters": {
        "color": 7,
        "width": 464,
        "height": 576,
        "content": "## \ud83d\udfe3 Append to Sheets + Send Email\n\n* \u2795 **HTTP Request** calls Google Sheets `values:append` with `valueInputOption=RAW`, `insertDataOption=INSERT_ROWS`, `includeValuesInResponse=true`, then \u2709\ufe0f **Gmail** sends the formatted attendance to the chosen recipient\n\n* \ud83d\udce5 Appends the received row into **Sheet1!A:Z** and formats the message where the first item is the date, the last item is the total, and the rest are numbered names\n\n\ud83e\uddea Example input:\n{\"majorDimension\":\"ROWS\",\"values\":[[\"2025-10-03\",\"6\",\"Camila Torres Rivera\",...,\"Anisah Anif\"]]}\n\n\n\ud83d\udcca Result: a new row with date in column A, total count in column B, then each attendee name across subsequent columns, and an email sent with subject `Attendance List`\n"
      },
      "typeVersion": 1
    },
    {
      "id": "e37ad5b4-1df1-4d4c-b07a-358ea7c2f8ef",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        432,
        -480
      ],
      "parameters": {
        "width": 416,
        "height": 576,
        "content": "## \ud83d\udfe1 VLM Run: Extraction Logic\n\n\n- \ud83e\udd16 **Execute Agent** processes the downloaded image.  \n- \ud83d\udcdd **Prompt**: Return JSON in format `{ \"majorDimension\": \"ROWS\", \"values\": [[\"YYYY-MM-DD\", \"user_count\", \"name1\", \"name2\", ...]] }`.  \n- \ud83c\udf10 Result posted to n8n webhook **`check-attendance`** (test endpoint).  \n- \ud83d\udce9 Webhook receives ready-to-append Sheets payload.  \n- \ud83e\udde0 *Example output*:  \n  `{\"majorDimension\":\"ROWS\",\"values\":[[\"2025-10-03\",\"6\",\"Camila Torres Rivera\",\"Mellissa\"]]}`\n\n\n"
      },
      "typeVersion": 1
    },
    {
      "id": "c4b6f7e3-31ff-49ff-9640-c90950141a33",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        0,
        -480
      ],
      "parameters": {
        "color": 7,
        "width": 416,
        "height": 576,
        "content": "## \ud83d\udfe2 Google Drive: Monitor & Download\n\n* \u23f1\ufe0f Google Drive Trigger checks the target folder every minute for **fileCreated** events\n\n* \ud83d\uddc2\ufe0f Trigger scope: the attendance images folder you selected\n\n* \ud83d\udd17 Passes the new file **id** to the next node\n\n* \u2b07\ufe0f Google Drive node downloads the file as binary under **data**\n\n* \ud83d\udce6 Output: the binary file is ready for the attendance extraction step\n"
      },
      "typeVersion": 1
    },
    {
      "id": "fbb0adca-34a1-41c0-847b-5fb7304f2c4a",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        0,
        -864
      ],
      "parameters": {
        "color": 7,
        "width": 848,
        "height": 368,
        "content": "## \ud83e\uddfe Attendance Extraction Pipeline\n\n**Overview:**\n\nUploads land in Google Drive \u2192 VLM Run\u2019s Execute Agent reads names from the image or scan \u2192 Agent posts a clean JSON to the n8n Webhook \u2192 The workflow appends the row into Google Sheets.\n\n\nPerfect for:\nWhiteboard standups, workshop sign-ins, classroom roll calls.\n\n\n**Requirements:**\n\n* VLM Run API credentials with access to Execute Agent\n* Google Drive OAuth2 for trigger and download\n* Google Sheets OAuth2 for `values:append`\n* n8n Webhook reachable at `check-attendance` on the test endpoint\n"
      },
      "typeVersion": 1
    },
    {
      "id": "3d69f4a9-20f0-487e-9402-cd7f2ec1bd73",
      "name": "Receives the list",
      "type": "n8n-nodes-base.webhook",
      "position": [
        704,
        -96
      ],
      "parameters": {
        "path": "check-attendance",
        "options": {},
        "httpMethod": "POST"
      },
      "typeVersion": 2.1
    },
    {
      "id": "0e9e1a63-80b6-4366-bbf9-dd18bfab4efb",
      "name": "Monitor List Uploads",
      "type": "n8n-nodes-base.googleDriveTrigger",
      "notes": "Monitors Google Drive folder for new receipt uploads and triggers processing automatically.",
      "position": [
        64,
        -96
      ],
      "parameters": {
        "event": "fileCreated",
        "options": {},
        "pollTimes": {
          "item": [
            {
              "mode": "everyMinute"
            }
          ]
        },
        "triggerOn": "specificFolder",
        "folderToWatch": {
          "__rl": true,
          "mode": "list",
          "value": "1S6baavqJn98MjUlbB6KtmARCWuWEekIZ",
          "cachedResultUrl": "https://drive.google.com/drive/folders/1S6baavqJn98MjUlbB6KtmARCWuWEekIZ",
          "cachedResultName": "test_data"
        }
      },
      "credentials": {
        "googleDriveOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "bcdf4ec9-602c-402b-a929-54a0b73f2d70",
      "name": "Download List",
      "type": "n8n-nodes-base.googleDrive",
      "notes": "Downloads receipt files from Google Drive for AI processing.",
      "position": [
        240,
        -96
      ],
      "parameters": {
        "fileId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $json.id }}"
        },
        "options": {
          "binaryPropertyName": "data"
        },
        "operation": "download"
      },
      "credentials": {
        "googleDriveOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 3
    },
    {
      "id": "93549d62-06ba-491d-bb82-c9c79beeca71",
      "name": "Append New Attendance to Sheet",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        944,
        -80
      ],
      "parameters": {
        "url": "https://sheets.googleapis.com/v4/spreadsheets/1lg0aJKvd7E2pbhumHNjcgxUfEQKvlBs9h1zZbhSeqas/values/Sheet1!A:Z:append",
        "method": "POST",
        "options": {},
        "jsonBody": "={{ $json.body.response }}",
        "sendBody": true,
        "sendQuery": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "authentication": "predefinedCredentialType",
        "queryParameters": {
          "parameters": [
            {
              "name": "valueInputOption",
              "value": "RAW"
            },
            {
              "name": "insertDataOption",
              "value": "INSERT_ROWS"
            },
            {
              "name": "includeValuesInResponse",
              "value": "true"
            }
          ]
        },
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "nodeCredentialType": "googleSheetsOAuth2Api"
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "bc39bbf9-24a1-422a-99d8-d318bb15c59c",
      "name": "Send a message",
      "type": "n8n-nodes-base.gmail",
      "position": [
        1168,
        -80
      ],
      "parameters": {
        "sendTo": "user@example.com",
        "message": "={{\n(() => {\n  const raw = $json.updates?.updatedData?.values;\n\n  // Normalize to a flat array of strings\n  const parts = Array.isArray(raw)\n    ? (Array.isArray(raw[0]) ? raw[0] : raw).map(String)\n    : String(raw || \"\").split(\",\").map(s => s.trim());\n\n  if (!parts.length) return \"No data received\";\n\n  const date = parts[0];\n  const total = parts[1];\n  const names = parts.slice(2).filter(Boolean);\n\n  // Optional pretty date without relying on timezone parsing\n  const prettyDate = /^(\\d{4})-(\\d{2})-(\\d{2})$/.test(date)\n    ? date.replace(/^(\\d{4})-(\\d{2})-(\\d{2})$/, \"$3-$2-$1\")\n    : date;\n\n  const list = names.map((n, i) => `${i + 1}. ${n}`).join(\"<br>\");\n\n  return `Today's Attendance List (${prettyDate})<br>Total: ${total}<br>${list}`;\n})()\n}}\n",
        "options": {},
        "subject": "Attendance List"
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "4c50399f-3901-4067-859c-241834696ea5",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        864,
        -640
      ],
      "parameters": {
        "color": 5,
        "width": 464,
        "height": 144,
        "content": "## \ud83d\udcdd Webhook: Set Public Callback URL\n\nPaste your n8n webhook\u2019s Production URL into VLM Run\u2019s Callback URL field\u2014no localhost URLs (they\u2019re unreachable)."
      },
      "typeVersion": 1
    }
  ],
  "connections": {
    "Download List": {
      "main": [
        [
          {
            "node": "VLM Run for Extraction",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Receives the list": {
      "main": [
        [
          {
            "node": "Append New Attendance to Sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Monitor List Uploads": {
      "main": [
        [
          {
            "node": "Download List",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Append New Attendance to Sheet": {
      "main": [
        [
          {
            "node": "Send a message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}

Credentials you'll need

Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.

Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

This workflow automates daily attendance tracking by analyzing uploaded attendance images, extracting participant names via VLM Run’s Execute Agent, appending the structured data into Google Sheets, and emailing a formatted attendance summary through Gmail. A Google Drive…

Source: https://n8n.io/workflows/9327/ — original creator credit. Request a take-down →

More Email & Gmail workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

Email & Gmail

Streamline and standardize your entire client onboarding process with a single end-to-end automation. 🚀📋 This workflow captures detailed client intake data via webhook, automatically creates a fully s

Slack, Asana, HTTP Request +4
Email & Gmail

This N8N template demonstrates using Foxit's Extraction API to get information from an incoming document and then using Diffbot's APIs to turn the text into a list of organizations mentioned in the do

Google Drive Trigger, Google Drive, HTTP Request +1
Email & Gmail

This automation template streamlines the process of capturing screenshots for multiple URLs. Instead of manually visiting each URL, taking a screenshot, and organizing the results, this workflow autom

HTTP Request, Google Sheets, Google Drive +2
Email & Gmail

✨🔪 Advanced AI Powered Document Parsing & Text Extraction with Llama Parse. Uses gmail, gmailTrigger, limit, stickyNote. Webhook trigger; 54 nodes.

Gmail, Gmail Trigger, HTTP Request +6
Email & Gmail

Wait. Uses httpRequest, itemLists, slack, gmail. Webhook trigger; 29 nodes.

HTTP Request, Item Lists, Slack +2