{
  "id": "Xwr38HF9nYUSqy4N",
  "name": "Scan business cards with LINE and Gemini, then send thank-you emails via Gmail",
  "tags": [
    {
      "id": "CPoKaUDm8iaNVfT5",
      "name": "Japan",
      "createdAt": "2026-03-26T10:40:30.577Z",
      "updatedAt": "2026-03-26T10:40:30.577Z"
    },
    {
      "id": "XEQR8pEbKRzbpFHH",
      "name": "Gemini",
      "createdAt": "2026-03-26T10:31:18.274Z",
      "updatedAt": "2026-03-26T10:31:18.274Z"
    },
    {
      "id": "Bw4qUxioHtXq3v2O",
      "name": "CRM",
      "createdAt": "2026-03-26T10:40:30.623Z",
      "updatedAt": "2026-03-26T10:40:30.623Z"
    },
    {
      "id": "xq8850GZ4YeKwowz",
      "name": "LINE",
      "createdAt": "2026-03-26T10:31:18.357Z",
      "updatedAt": "2026-03-26T10:31:18.357Z"
    }
  ],
  "nodes": [
    {
      "id": "7238e443-39b8-4342-9c6f-3c0233b0bccd",
      "name": "LINE Webhook",
      "type": "n8n-nodes-base.webhook",
      "position": [
        640,
        528
      ],
      "parameters": {
        "path": "line-webhook",
        "options": {},
        "httpMethod": "POST",
        "responseMode": "responseNode"
      },
      "typeVersion": 2
    },
    {
      "id": "c5a5b582-177b-402c-b96d-8d858f423e8a",
      "name": "Respond to LINE immediately",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        848,
        416
      ],
      "parameters": {
        "options": {},
        "respondWith": "text",
        "responseBody": "OK"
      },
      "typeVersion": 1
    },
    {
      "id": "b574f6b5-83c3-40f9-a0de-902d25784a70",
      "name": "Set config values",
      "type": "n8n-nodes-base.set",
      "position": [
        848,
        528
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "cfg-001",
              "name": "config.lineChannelAccessToken",
              "type": "string",
              "value": "User Key"
            },
            {
              "id": "cfg-002",
              "name": "config.slackChannel",
              "type": "string",
              "value": "User Key"
            },
            {
              "id": "cfg-003",
              "name": "config.googleSheetId",
              "type": "string",
              "value": "User Key"
            },
            {
              "id": "cfg-004",
              "name": "config.thankYouEmailSubject",
              "type": "string",
              "value": "User Key"
            },
            {
              "id": "cfg-005",
              "name": "config.replyMessageSuccess",
              "type": "string",
              "value": "\u540d\u523a\u3092\u767b\u9332\u3057\u307e\u3057\u305f\uff01Google Sheets\u306b\u4fdd\u5b58\u3055\u308c\u3066\u3044\u307e\u3059\u3002"
            },
            {
              "id": "cfg-006",
              "name": "config.replyMessageNoImage",
              "type": "string",
              "value": "\u540d\u523a\u306e\u753b\u50cf\u3092\u9001\u3063\u3066\u304f\u3060\u3055\u3044\u3002"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "38d99fed-61d0-4274-b264-1c57d472fe72",
      "name": "Is image message?",
      "type": "n8n-nodes-base.if",
      "position": [
        1072,
        528
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 1,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "cond-002",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $('LINE Webhook').item.json.body.events[0].message.type }}",
              "rightValue": "image"
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "2272fa5b-ec32-493c-8d49-9afbf73f8ee7",
      "name": "Download image from LINE",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1296,
        448
      ],
      "parameters": {
        "url": "=https://api-data.line.me/v2/bot/message/{{ $('LINE Webhook').item.json.body.events[0].message.id }}/content",
        "options": {
          "response": {
            "response": {
              "responseFormat": "file"
            }
          }
        },
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "=Bearer {{ $('Set config values').item.json.config.lineChannelAccessToken }}"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "81cb7687-969f-4da8-8099-2c025671c113",
      "name": "Parse Gemini JSON response",
      "type": "n8n-nodes-base.code",
      "position": [
        1968,
        448
      ],
      "parameters": {
        "jsCode": "const raw = $input.item.json.text;\nconst cleaned = raw.replace(/```json\\n?/g, '').replace(/```/g, '').trim();\ntry {\n  const parsed = JSON.parse(cleaned);\n  return { json: parsed };\n} catch(e) {\n  return { json: { \n    full_name: '', company: '', department: '',\n    title: '', email: '', phone: '',\n    address: '', website: '',\n    notes: 'Parse error: ' + raw\n  }};\n}"
      },
      "typeVersion": 2
    },
    {
      "id": "20e0483a-bc1b-4f9e-b260-bd8397a850cf",
      "name": "Save contact to Google Sheets",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        2192,
        448
      ],
      "parameters": {
        "columns": {
          "value": {
            "Email": "={{ $json.email }}",
            "Notes": "={{ $json.notes }}",
            "Phone": "={{ $json.phone }}",
            "Title": "={{ $json.title }}",
            "Address": "={{ $json.address }}",
            "Company": "={{ $json.company }}",
            "Website": "={{ $json.website }}",
            "Full name": "={{ $json.full_name }}",
            "Timestamp": "={{ $now.toFormat('yyyy-MM-dd HH:mm:ss') }}",
            "Department": "={{ $json.department }}"
          },
          "schema": [
            {
              "id": "Timestamp",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Timestamp",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Full name",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Full name",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Company",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Company",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Department",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Department",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Title",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Title",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Email",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Email",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Phone",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Phone",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Address",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Address",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Website",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Website",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "LINE user ID",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "LINE user ID",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Notes",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Notes",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "\u30b7\u30fc\u30c81"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('Set config values').item.json.config.googleSheetId }}"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "d75fa8ba-2339-4caf-ba89-d5df54f91df9",
      "name": "Notify team on Slack",
      "type": "n8n-nodes-base.slack",
      "position": [
        2512,
        368
      ],
      "parameters": {
        "text": "=:business_card: *\u65b0\u3057\u3044\u540d\u523a\u304c\u767b\u9332\u3055\u308c\u307e\u3057\u305f*\n\n*\u6c0f\u540d:* {{ $('Parse Gemini JSON response').item.json.full_name || '(\u4e0d\u660e)' }}\n*\u4f1a\u793e:* {{ $('Parse Gemini JSON response').item.json.company || '(\u4e0d\u660e)' }}\n*\u5f79\u8077:* {{ $('Parse Gemini JSON response').item.json.title || '(\u4e0d\u660e)' }}\n*\u30e1\u30fc\u30eb:* {{ $('Parse Gemini JSON response').item.json.email || '(\u306a\u3057)' }}\n*\u96fb\u8a71:* {{ $('Parse Gemini JSON response').item.json.phone || '(\u306a\u3057)' }}\n\n:link: <https://docs.google.com/spreadsheets/d/{{ $('Set config values').item.json.config.googleSheetId }}|Google Sheets\u3067\u78ba\u8a8d>",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "name",
          "value": "={{ $('Set config values').item.json.config.slackChannel }}"
        },
        "otherOptions": {},
        "authentication": "oAuth2"
      },
      "typeVersion": 2.3
    },
    {
      "id": "78e019c6-3c1d-4343-8cfa-165c87423140",
      "name": "Has email address?",
      "type": "n8n-nodes-base.if",
      "position": [
        2496,
        528
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 1,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "cond-003",
              "operator": {
                "type": "string",
                "operation": "notEquals"
              },
              "leftValue": "={{ $('Parse Gemini JSON response').item.json.email }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "38a2ca30-ef09-4eca-a163-efa180322e0e",
      "name": "Send thank-you email via Gmail",
      "type": "n8n-nodes-base.gmail",
      "position": [
        2768,
        448
      ],
      "parameters": {
        "sendTo": "={{ $json.Email }}",
        "message": "=Dear {{ $('Parse Gemini JSON response').item.json.full_name }},\n\nThank you for connecting with us. It was a pleasure meeting you.\n\nWe have saved your contact information and will be in touch soon.\n\nBest regards",
        "options": {},
        "subject": "={{ $('Set config values').item.json.config.thankYouEmailSubject }}",
        "emailType": "text"
      },
      "typeVersion": 2.1
    },
    {
      "id": "c7ef357d-8636-494b-93f4-b4511feef860",
      "name": "Reply success message on LINE",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        3024,
        544
      ],
      "parameters": {
        "url": "https://api.line.me/v2/bot/message/reply",
        "body": "={\n  \"replyToken\": \"{{ $('LINE Webhook').item.json.body.events[0].replyToken }}\",\n  \"messages\": [\n    {\n      \"type\": \"text\",\n      \"text\": \"{{ $('Set config values').item.json.config.replyMessageSuccess }}\"\n    }\n  ]\n}",
        "method": "POST",
        "options": {},
        "sendBody": true,
        "contentType": "raw",
        "sendHeaders": true,
        "rawContentType": "application/json",
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "=Bearer {{ $('Set config values').item.json.config.lineChannelAccessToken }}"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "aad891ae-c3ef-4cfe-9f98-1e239b073ebe",
      "name": "Reply no-image message on LINE",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1296,
        640
      ],
      "parameters": {
        "url": "https://api.line.me/v2/bot/message/reply",
        "body": "={\n  \"replyToken\": \"{{ $('LINE Webhook').item.json.body.events[0].replyToken }}\",\n  \"messages\": [\n    {\n      \"type\": \"text\",\n      \"text\": \"{{ $('Set config values').item.json.config.replyMessageNoImage }}\"\n    }\n  ]\n}",
        "method": "POST",
        "options": {},
        "sendBody": true,
        "contentType": "raw",
        "sendHeaders": true,
        "rawContentType": "application/json",
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "=Bearer {{ $('Set config values').item.json.config.lineChannelAccessToken }}"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "9632d432-c5f6-470a-ba4b-9365459099af",
      "name": "Google Gemini Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
      "position": [
        1536,
        656
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 1
    },
    {
      "id": "20ce84b9-0ffc-4f7d-947e-cfc0d2eebdf8",
      "name": "Extract business card data with Gemini",
      "type": "@n8n/n8n-nodes-langchain.chainLlm",
      "position": [
        1504,
        448
      ],
      "parameters": {
        "text": "=You are a business card OCR assistant.\nExtract information from the business card image and return ONLY a valid JSON object with no additional text, markdown, or explanation.\n\nRequired JSON format:\n{\n  \"full_name\": \"\",\n  \"company\": \"\",\n  \"department\": \"\",\n  \"title\": \"\",\n  \"email\": \"\",\n  \"phone\": \"\",\n  \"address\": \"\",\n  \"website\": \"\",\n  \"notes\": \"\"\n}\n\nRules:\n- If a field is not found, use an empty string \"\"\n- For phone, include country code if visible (e.g. +81-3-xxxx-xxxx)\n- For Japanese names, romanize if only kanji/kana is present\n- Return only the JSON object, nothing else\n\n{{ $json.data }}",
        "batching": {},
        "messages": {
          "messageValues": [
            {
              "type": "HumanMessagePromptTemplate",
              "messageType": "imageBinary"
            }
          ]
        },
        "promptType": "define"
      },
      "typeVersion": 1.9
    },
    {
      "id": "3fd4b151-2e63-45fd-a9c5-0cbe8f794e9a",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        0,
        0
      ],
      "parameters": {
        "width": 608,
        "height": 720,
        "content": "Got a stack of meishi from your last networking event? Just snap a photo and send it to LINE \u2014 this workflow handles the rest.\n\n### How it works\n\nThe LINE webhook picks up your image, checks it's actually a photo (not a text message), then passes it straight to Gemini Vision for OCR. Gemini pulls out the key fields \u2014 name, company, title, email, phone, address \u2014 and the result gets appended as a new row in your Google Sheet. Meanwhile, Slack gets a ping with the contact summary, Gmail fires off a thank-you email if an address was found, and LINE confirms back to you that it's all saved.\n\n### Setup steps\n\n1. Add your LINE Messaging API credentials in n8n and paste the webhook URL into the LINE Developer Console\n2. Drop in a Google Gemini API key \u2014 the free tier is fine for this\n3. Share your target Google Sheet with the service account email, or connect via OAuth\n4. Open the \"Notify team on Slack\" node and set your channel name\n5. Connect Gmail via OAuth in the credentials panel\n6. Send a test business card photo to your LINE bot and check the Sheet\n\n### Customization\n\nTweak the Gemini prompt in \"Extract business card data with Gemini\" to capture extra fields like LinkedIn URL or department. Adjust the Slack message blocks or the Gmail body to match your team's tone."
      },
      "typeVersion": 1
    },
    {
      "id": "681d606b-df05-4849-9d27-279c0d80db0a",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        800,
        16
      ],
      "parameters": {
        "color": 7,
        "width": 464,
        "height": 240,
        "content": "## Receive & validate\n\nPicks up the LINE message and checks that an image was actually attached before doing anything else."
      },
      "typeVersion": 1
    },
    {
      "id": "d225e2c5-8353-44ce-b3fe-363626d3a7bc",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1328,
        32
      ],
      "parameters": {
        "color": 7,
        "width": 544,
        "height": 272,
        "content": "## Extract contact data\n\nDownloads the image from LINE and runs it through Gemini Vision to pull out structured contact fields."
      },
      "typeVersion": 1
    },
    {
      "id": "91d8d175-d15b-40a5-b37e-c8baa49ba57a",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1984,
        48
      ],
      "parameters": {
        "color": 7,
        "width": 368,
        "height": 240,
        "content": "## Save to Sheets\n\nParses the Gemini response and appends a new contact row to your Google Sheet."
      },
      "typeVersion": 1
    },
    {
      "id": "46750f77-ceec-4e9e-b731-35217a4b0ae7",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2464,
        96
      ],
      "parameters": {
        "color": 7,
        "width": 368,
        "height": 192,
        "content": "## Notify & confirm\n\nPings Slack with the contact summary, sends a Gmail thank-you if an email was found, then confirms back to the user on LINE."
      },
      "typeVersion": 1
    },
    {
      "id": "84634727-9d94-42d0-acb1-45173f012794",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2880,
        176
      ],
      "parameters": {
        "color": 7,
        "width": 320,
        "height": 192,
        "content": "## \ud83d\udce7 Send Email\nIf an email address was found on the business card, Gmail automatically sends a thank-you email to the new contact."
      },
      "typeVersion": 1
    },
    {
      "id": "271aaad2-fcb3-4d0e-9d91-3a98f03a714c",
      "name": "Sticky Note6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2848,
        784
      ],
      "parameters": {
        "color": 7,
        "width": 320,
        "height": 192,
        "content": "## \ud83d\udd14 Slack Notification\nPosts the extracted contact details to a Slack channel to keep the whole team in the loop."
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "binaryMode": "separate",
    "executionOrder": "v1"
  },
  "versionId": "45980dea-9bc4-422d-bc3d-c812ab4a1efb",
  "connections": {
    "LINE Webhook": {
      "main": [
        [
          {
            "node": "Respond to LINE immediately",
            "type": "main",
            "index": 0
          },
          {
            "node": "Set config values",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Is image message?": {
      "main": [
        [
          {
            "node": "Download image from LINE",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Reply no-image message on LINE",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set config values": {
      "main": [
        [
          {
            "node": "Is image message?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Has email address?": {
      "main": [
        [
          {
            "node": "Send thank-you email via Gmail",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Reply success message on LINE",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Notify team on Slack": {
      "main": [
        [
          {
            "node": "Send thank-you email via Gmail",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Download image from LINE": {
      "main": [
        [
          {
            "node": "Extract business card data with Gemini",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Google Gemini Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "Extract business card data with Gemini",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Parse Gemini JSON response": {
      "main": [
        [
          {
            "node": "Save contact to Google Sheets",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Save contact to Google Sheets": {
      "main": [
        [
          {
            "node": "Notify team on Slack",
            "type": "main",
            "index": 0
          },
          {
            "node": "Has email address?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send thank-you email via Gmail": {
      "main": [
        [
          {
            "node": "Reply success message on LINE",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract business card data with Gemini": {
      "main": [
        [
          {
            "node": "Parse Gemini JSON response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}