{
  "id": "yzYmkHXckHPtae5x",
  "meta": {
    "templateId": "4300",
    "templateCredsSetupCompleted": true
  },
  "name": "Template_OCR Business Card Reader with Thai Support \u2013 Enrich & Save Contacts to CRM Spreadsheet",
  "tags": [],
  "nodes": [
    {
      "id": "e0db3554-f691-4650-a4d6-6116976dfad9",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        224,
        -800
      ],
      "parameters": {
        "color": 4,
        "width": 2840,
        "height": 808,
        "content": "## Version 1: Without Search API\n\n1. Trigger \u2013 Upload a business card image.\n\n2. Typhoon OCR \u2013 Extracts text from the card. Edit Image node prior to this node is needed.\n\n3. Typhoon LLM (Parser) \u2013 Converts the raw text into a structured contact fields in JSON format.\n\n4. Typhoon LLM (Enricher) \u2013 Adds extra fields such as job type, job level, and sector.\n\n\u2728 Adjust the prompt here to match your business needs.\n\n5. Typhoon LLM (Email Draft) \u2013 Generates a short greeting email (in case needed)\n\n6. Google Sheets \u2013 Saves the structured contact information.\n\n7. Optional Email Step \u2013 If you\u2019d like to send the greeting email:\n- Connect the Text to JSON and Gmail nodes.\n- Leave them disconnected if you don\u2019t need this feature.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "0057ad5a-e36a-41a3-9bcc-ed0cf1062dd9",
      "name": "Typhoon OCR",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "position": [
        864,
        -128
      ],
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "id",
          "value": "typhoon-ocr-preview-techsauce-workshop"
        },
        "options": {
          "temperature": 0.1
        }
      },
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "5eafbe54-990d-492b-9fe3-7aa107ed9bc4",
      "name": "Typhoon 2.1 Gemma1",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "position": [
        1264,
        -128
      ],
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "id",
          "value": "typhoon-v2.1-12b-instruct-techsauce-workshop"
        },
        "options": {
          "temperature": 0.4
        }
      },
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "fc973c4a-e1b9-42d7-8e9b-bd87bd69f76f",
      "name": "OCR Agent",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        768,
        -352
      ],
      "parameters": {
        "text": "=Below is an image of a document page, along with its dimensions and possibly some raw textual content previously extracted from it. Note that the text extraction may be incomplete or partially missing. Carefully consider both the layout and any available text to reconstruct the document accurately.\nYour task is to return the markdown representation of this document, presenting tables in HTML format as they naturally appear.\nIf the document contains images or figures, analyze them and include the tag <figure>IMAGE_ANALYSIS</figure> in the appropriate location.\nYour final output must be in JSON format with a single key `natural_text` containing the response.\nRAW_TEXT_START\nPage dimensions: {{ $json.size.width }}.0x{{ $json.size.height }}.0\n[Image 0x0 to {{ $json.size.width }}x{{ $json.size.height }}]\nRAW_TEXT_END",
        "options": {
          "passthroughBinaryImages": true
        },
        "promptType": "define"
      },
      "typeVersion": 2.1
    },
    {
      "id": "0d7d718e-89d1-4a15-82f4-e558cf9288de",
      "name": "Contact Info Parser",
      "type": "@n8n/n8n-nodes-langchain.chainLlm",
      "position": [
        1168,
        -352
      ],
      "parameters": {
        "text": "=Extract the following information from the provided text and format as JSON:\n\n**Text to process:**\n{{ $json.output }}\n\n**Required JSON fields:**\n- `first_name_en` - First name in English if exists\n- `first_name_th` - First name in Thai if exists\n- `last_name_en` - Last name in English if exists \n- `last_name_th` - Last name in Thai if exists\n- `nick_name_en` - Nick name in English if exists\n- `nick_name_th` - Nick name in Thai if exists\n- `job_title_en` - Job title in English if exists\n- `job_title_th` - Job title in Thai if exists\n- `organization_en` - Organization name in English if exists\n- `organization_th` - Organization name in Thai if exists\n- `email` - Email address\n- `phone` - Phone number\n- `website` - Website URL\n\n**Instructions:**\n- Return only valid JSON without code fences, markdown, or explanatory text\n- Use `null` as the value for missing information\n- Do not copy `first_name_en` OR `last_name_en` to `first_name_th` OR `last_name_th` name, and vice versa\n- If there is a name in parentheses and it is before last name or after last name, you can assume that it is the nick name. Strip out the parenthesis before parsing as nick_name\n- Preserve original formatting for names and titles\n- If there is a country code for phone number, make sure to remove the plus (+) sign and put the code in parentheses. For example, if the phone number contains +66 you will parse it as (66)\n- Remove spaces between numbers in phone number\n- Ensure all field names match exactly as specified above\n\n**Expected output format:**\n```json\n{\n  \"first_name_en\": \"value\",\n  \"first_name_th\": \"value\",\n  \"last_name_en\": \"value\",\n  \"last_name_th\": \"value\",\n  \"nick_name_en\": \"value\",\n  \"nick_name_th\": \"value\",\n  \"job_title_en\": \"value\", \n  \"job_title_th\": \"value\",\n  \"organization_en\": \"value\",\n  \"organization_th\": \"value\",\n  \"email\": \"value\",\n  \"phone\": \"value\",\n  \"website\": \"value\"\n}\n```",
        "batching": {},
        "messages": {
          "messageValues": [
            {
              "message": "=You are a smart assistant that extracts contact information from messy text (e.g. OCR results from a business card).  \n\nInstructions: \n- The input text may include either or both English and Thai. Carefully identify the language (Thai or English) for each piece of information.\n - If both English and Thai versions appear, fill both fields.\n- If not, fill in only the relevant language fields and use 'null' for missing information\n- Only capitalize the first letter of `first_name_en` and `last_name_en` and `nick_name_en`\n - Put the text in the correct language field. For example, if the name is in Thai, fill first_name_th / last_name_th / nick_name_th, and vice versa\n- For website, if not explicitly given, just imply it from an email domain. However, you should do so only if the email domain appears to be a company domain, not a free domain like gmail, outlook, etc. Otherwise, just leave it blank."
            }
          ]
        },
        "promptType": "define"
      },
      "typeVersion": 1.7
    },
    {
      "id": "a6887325-f2c0-4a22-b736-60cc5025b4a8",
      "name": "Contact Info Enricher",
      "type": "@n8n/n8n-nodes-langchain.chainLlm",
      "position": [
        1568,
        -352
      ],
      "parameters": {
        "text": "=Given this original JSON {{ $json.text }}, your task is to classify and return the following additional fields in JSON:\n\n1. job_type\nPossible values: Technical, Business, Both\n\n2. job_level\nPossible values: Junior, Senior, Manager/Director, Executive\n\n3. sector\nPossible values:\n- Consulting / System Integrator\n- Academic\n- Startup\n- Enterprise\n- Government\n\nReturn only the JSON object. Do not include any code fences or markdown.\n",
        "batching": {},
        "messages": {
          "messageValues": [
            {
              "message": "You are an assistant that classifies business contacts into marketing categories. Instructions: - Use job title, organization name, email domain, and website to make the best classification. - Carefully infer the likely job_type from job title. E.g., technical roles like engineer, developer = Technical; sales or marketing = Business; roles blending both = Both - Infer job_level from job title: Junior, Senior, Manager/Director, Executive. - Infer sector from organization name or email domain: Academic for universities or schools; Government for ministries or departments; Startup for small tech companies; Enterprise for large companies; Consulting/System Integrator for consultancies. - If you cannot confidently assign a field, leave it as an empty string."
            }
          ]
        },
        "promptType": "define"
      },
      "typeVersion": 1.7
    },
    {
      "id": "846ba69b-80bc-4a9d-af57-173da37ac92c",
      "name": "Add Contact to Google Sheet",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        2624,
        -352
      ],
      "parameters": {
        "columns": {
          "value": {
            "text": "={{ $('On form submission').item.json['Message (Optional)'] }}",
            "email": "={{ $json.contact_obj.email }}",
            "phone": "={{ $json.contact_obj.phone }}",
            "sector": "={{ $json.contact_obj.sector }}",
            "website": "={{ $json.contact_obj.website }}",
            "job_type": "={{ $json.contact_obj.job_type }}",
            "job_level": "={{ $json.contact_obj.job_level }}",
            "job_title_en": "={{ $json.contact_obj.job_title_en }}",
            "job_title_th": "={{ $json.contact_obj.job_title_th }}",
            "last_name_en": "={{ $json.contact_obj.last_name_en }}",
            "last_name_th": "={{ $json.contact_obj.last_name_th }}",
            "nick_name_en": "={{ $json.contact_obj.nick_name_en }}",
            "nick_name_th": "={{ $json.contact_obj.nick_name_th }}",
            "first_name_en": "={{ $json.contact_obj.first_name_en }}",
            "first_name_th": "={{ $json.contact_obj.first_name_th }}",
            "organization_en": "={{ $json.contact_obj.organization_en }}",
            "organization_th": "={{ $json.contact_obj.organization_th }}"
          },
          "schema": [
            {
              "id": "first_name_en",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "first_name_en",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "last_name_en",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "last_name_en",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "first_name_th",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "first_name_th",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "last_name_th",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "last_name_th",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "nick_name_en",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "nick_name_en",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "nick_name_th",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "nick_name_th",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "email",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "email",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "phone",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "phone",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "job_title_en",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "job_title_en",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "job_title_th",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "job_title_th",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "job_type",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "job_type",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "job_level",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "job_level",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "organization_en",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "organization_en",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "organization_th",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "organization_th",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "sector",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "sector",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "website",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "website",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "text",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "text",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1UleIustQHKLHjImasGFFt3ZPi26Uv7JEEVJ6a39uMwI/edit#gid=0",
          "cachedResultName": "Workshop Template"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1UleIustQHKLHjImasGFFt3ZPi26Uv7JEEVJ6a39uMwI",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1UleIustQHKLHjImasGFFt3ZPi26Uv7JEEVJ6a39uMwI/edit?usp=drivesdk",
          "cachedResultName": "BizCard Contacts"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.6
    },
    {
      "id": "67ef3890-7b46-446c-81e7-5e0db4627e01",
      "name": "Greeting Email Composer1",
      "type": "@n8n/n8n-nodes-langchain.chainLlm",
      "position": [
        2048,
        -352
      ],
      "parameters": {
        "text": "=Write a short, friendly greeting email to the person whose name card was collected at an event or a workshop (Techsauce Global Summit 2025) and format the email into a JSON object with two fields:\\n\n- \\\"subject\\\": the email subject\\n   \n- \\\"body\\\": the message content\\n\\n\nYou have access to structured data from a name card,{{ $('Text to JSON') .item.json.contact_obj.toJsonString() }}, including the recipient\u2019s first name (which you can use to greet them). Other details may be available but are not guaranteed.\nYou also have access to the name card\u2019s owner message that is left when they gave us the name card. {{ $('On form submission').item.json['Message (Optional)'] }}. The email contain should acknowledge the message received and respond with helpful information.\nDo not include markdown, explanations, or code blocks. Your email MUST BE IN ENGLISH.\nAt the end of the email content, also add that this email sender is not an official email and is used for the automation workflow demo purpose only. If the recipient want to contact Typhoon team formally, please send an email or reply back to \"contact@opentyphoon.ai\"",
        "batching": {},
        "messages": {
          "messageValues": [
            {
              "message": "You are a warm, helpful, and professional marketing/community representative from Typhoon. Your task is to write a personalized greeting email based on given information in the user prompt.   Instructions:  1. If `first_name` is available, greet the recipient using it. If not, use a general greeting.\\n  2. Acknowledge the recent contact (e.g. we received your name card)\\n  3. Acknowledge the contact\u2019s message or questions if provided in the prompt (optional)\\n   4. Invite follow-up or continued conversation\\n  5. Keep it short, clear, and friendly. Do not assume things you don\u2019t know for sure and add too much personalization based on all the fields you\u2019re givena\\n 6. Email content must be complete. Do not add boilerplate or incomplete content pieces. 7. Return only a valid JSON object with \\\"headline\\\" and \\\"body\\\". "
            }
          ]
        },
        "promptType": "define"
      },
      "typeVersion": 1.7
    },
    {
      "id": "e3f1f80e-382c-4c63-979e-eb3fd94db039",
      "name": "Send a message",
      "type": "n8n-nodes-base.gmail",
      "position": [
        3456,
        80
      ],
      "parameters": {
        "sendTo": "={{ $('Text to JSON').item.json.contact_obj.email }}",
        "message": "={{ $json.contact_obj.body }}",
        "options": {
          "appendAttribution": false
        },
        "subject": "={{ $json.contact_obj.subject }}"
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "bf27f2e5-b4a4-48e6-8011-d74727e58937",
      "name": "Edit Image",
      "type": "n8n-nodes-base.editImage",
      "position": [
        544,
        -352
      ],
      "parameters": {
        "operation": "information",
        "dataPropertyName": "={{ $json.parents[0] }}"
      },
      "typeVersion": 1
    },
    {
      "id": "756142bb-26de-4db5-809c-fb98e6a5dbc2",
      "name": "Text to JSON 2",
      "type": "n8n-nodes-base.set",
      "position": [
        2384,
        -352
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "d07ce0e8-330a-49fc-b5a3-20decd56c00d",
              "name": "contact_obj",
              "type": "object",
              "value": "={{ $json.text.replaceAll(\"```json\", \"\").replaceAll(\"```\", \"\").trim().parseJson() }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "ba6190d3-ff24-4d62-8a64-18239fe210d2",
      "name": "On form submission1",
      "type": "n8n-nodes-base.formTrigger",
      "position": [
        288,
        480
      ],
      "parameters": {
        "options": {},
        "formTitle": "Name card collector",
        "formFields": {
          "values": [
            {
              "fieldType": "file",
              "fieldLabel": "nameCard",
              "multipleFiles": false,
              "requiredField": true,
              "acceptFileTypes": ".jpg, .png, .jpeg"
            },
            {
              "fieldType": "textarea",
              "fieldLabel": "Message (Optional)"
            }
          ]
        },
        "formDescription": "Upload your name card and message (optional) here. We will get back to you soon."
      },
      "typeVersion": 2.2
    },
    {
      "id": "c82c8637-d392-4739-a236-15b13f31316f",
      "name": "Typhoon OCR1",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "position": [
        832,
        704
      ],
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "id",
          "value": "typhoon-ocr-preview"
        },
        "options": {
          "temperature": 0.1
        }
      },
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "29a46645-4fb0-4db2-a7c8-e0de640c7c86",
      "name": "Typhoon 2.1 Gemma2",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "position": [
        1232,
        704
      ],
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "id",
          "value": "typhoon-v2.1-12b-instruct"
        },
        "options": {
          "temperature": 0.4
        }
      },
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "02449c4d-b30b-4599-8ac1-32c3af6c52d0",
      "name": "OCR Agent1",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        736,
        480
      ],
      "parameters": {
        "text": "=Below is an image of a document page, along with its dimensions and possibly some raw textual content previously extracted from it. Note that the text extraction may be incomplete or partially missing. Carefully consider both the layout and any available text to reconstruct the document accurately.\nYour task is to return the markdown representation of this document, presenting tables in HTML format as they naturally appear.\nIf the document contains images or figures, analyze them and include the tag <figure>IMAGE_ANALYSIS</figure> in the appropriate location.\nYour final output must be in JSON format with a single key `natural_text` containing the response.\nRAW_TEXT_START\nPage dimensions: {{ $json.size.width }}.0x{{ $json.size.height }}.0\n[Image 0x0 to {{ $json.size.width }}x{{ $json.size.height }}]\nRAW_TEXT_END",
        "options": {
          "passthroughBinaryImages": true
        },
        "promptType": "define"
      },
      "typeVersion": 2.1
    },
    {
      "id": "3a8ae88a-de0f-4e9b-9fca-80895bb0eb75",
      "name": "Contact Info Parser1",
      "type": "@n8n/n8n-nodes-langchain.chainLlm",
      "position": [
        1136,
        480
      ],
      "parameters": {
        "text": "=Extract the following information from the provided text and format as JSON:\n\n**Text to process:**\n{{ $json.output }}\n\n**Required JSON fields:**\n- `first_name_en` - First name in English if exists\n- `first_name_th` - First name in Thai if exists\n- `last_name_en` - Last name in English if exists \n- `last_name_th` - Last name in Thai if exists\n- `nick_name_en` - Nick name in English if exists\n- `nick_name_th` - Nick name in Thai if exists\n- `job_title_en` - Job title in English if exists\n- `job_title_th` - Job title in Thai if exists\n- `organization_en` - Organization name in English if exists\n- `organization_th` - Organization name in Thai if exists\n- `email` - Email address\n- `phone` - Phone number\n- `website` - Website URL\n\n**Instructions:**\n- Return only valid JSON without code fences, markdown, or explanatory text\n- Use `null` as the value for missing information\n- Do not copy `first_name_en` OR `last_name_en` to `first_name_th` OR `last_name_th` name, and vice versa\n- If there is a name in parentheses and it is before last name or after last name, you can assume that it is the nick name. Strip out the parenthesis before parsing as nick_name\n- Preserve original formatting for names and titles\n- If there is a country code for phone number, make sure to remove the plus (+) sign and put the code in parentheses. For example, if the phone number contains +66 you will parse it as (66)\n- Remove spaces between numbers in phone number\n- Ensure all field names match exactly as specified above\n\n**Expected output format:**\n```json\n{\n  \"first_name_en\": \"value\",\n  \"first_name_th\": \"value\",\n  \"last_name_en\": \"value\",\n  \"last_name_th\": \"value\",\n  \"nick_name_en\": \"value\",\n  \"nick_name_th\": \"value\",\n  \"job_title_en\": \"value\", \n  \"job_title_th\": \"value\",\n  \"organization_en\": \"value\",\n  \"organization_th\": \"value\",\n  \"email\": \"value\",\n  \"phone\": \"value\",\n  \"website\": \"value\"\n}\n```",
        "batching": {},
        "messages": {
          "messageValues": [
            {
              "message": "=You are a smart assistant that extracts contact information from messy text (e.g. OCR results from a business card).  \n\nInstructions: \n- The input text may include either or both English and Thai. Carefully identify the language (Thai or English) for each piece of information.\n - If both English and Thai versions appear, fill both fields.\n- If not, fill in only the relevant language fields and use 'null' for missing information\n- Only capitalize the first letter of `first_name_en` and `last_name_en` and `nick_name_en`\n - Put the text in the correct language field. For example, if the name is in Thai, fill first_name_th / last_name_th / nick_name_th, and vice versa\n- For website, if not explicitly given, just imply it from an email domain. However, you should do so only if the email domain appears to be a company domain, not a free domain like gmail, outlook, etc. Otherwise, just leave it blank."
            }
          ]
        },
        "promptType": "define"
      },
      "typeVersion": 1.7
    },
    {
      "id": "4d0c2e41-1f82-4ba5-a7e9-2e37f7536a40",
      "name": "Greeting Email Composer",
      "type": "@n8n/n8n-nodes-langchain.chainLlm",
      "position": [
        2176,
        480
      ],
      "parameters": {
        "text": "=Write a short, friendly greeting email to the person whose name card was collected at an event or a workshop (Techsauce Global Summit 2025) and format the email into a JSON object with two fields:\\n\n- \\\"subject\\\": the email subject\\n   \n- \\\"body\\\": the message content\\n\\n\nYou have access to structured data from a name card,{{ $('Text to JSON2') .item.json.contact_obj.toJsonString() }}, including the recipient\u2019s first name (which you can use to greet them). Other details may be available but are not guaranteed.\nYou also have access to the name card\u2019s owner message that is left when they gave us the name card, {{ $('On form submission1').item.json['Message (Optional)'] }}. The email contain should acknowledge the message received and respond with helpful information.\nDo not include markdown, explanations, or code blocks. Your email MUST BE IN ENGLISH.\nAt the end of the email content, also add that this email sender is not an official email and is used for the automation workflow demo purpose only. If the recipient want to contact Typhoon team formally, please send an email or reply back to \"contact@opentyphoon.ai\"",
        "batching": {},
        "messages": {
          "messageValues": [
            {
              "message": "You are a warm, helpful, and professional marketing/community representative from Typhoon. Your task is to write a personalized greeting email based on given information in the user prompt.   Instructions:  1. If `first_name` is available, greet the recipient using it. If not, use a general greeting.\\n  2. Acknowledge the recent contact (e.g. we received your name card)\\n  3. Acknowledge the contact\u2019s message or questions if provided in the prompt (optional)\\n   4. Invite follow-up or continued conversation\\n  5. Keep it short, clear, and friendly. Do not assume things you don\u2019t know for sure and add too much personalization based on all the fields you\u2019re givena\\n 6. Email content must be complete. Do not add boilerplate or incomplete content pieces. 7. Return only a valid JSON object with \\\"headline\\\" and \\\"body\\\". "
            }
          ]
        },
        "promptType": "define"
      },
      "typeVersion": 1.7
    },
    {
      "id": "129e4a62-233c-4916-b05b-9a50fd91605d",
      "name": "Edit Image1",
      "type": "n8n-nodes-base.editImage",
      "position": [
        512,
        480
      ],
      "parameters": {
        "operation": "information",
        "dataPropertyName": "nameCard"
      },
      "typeVersion": 1
    },
    {
      "id": "8681cc2e-c443-478a-9cf5-85e036c36fb2",
      "name": "Text to JSON ",
      "type": "n8n-nodes-base.set",
      "position": [
        2576,
        480
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "d07ce0e8-330a-49fc-b5a3-20decd56c00d",
              "name": "contact_obj",
              "type": "object",
              "value": "={{ $json.text.replaceAll(\"```json\", \"\").replaceAll(\"```\", \"\").trim().parseJson() }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "f73d3276-dba0-44da-be86-98adb5238e4f",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        224,
        48
      ],
      "parameters": {
        "color": 5,
        "width": 2824,
        "height": 840,
        "content": "## Version 2: With Search API\n\n1. Trigger: Form submission with name card\n\n2. Typhoon OCR extracts text. Edit Image node prior to this node is needed.\n\n3. Typhoon LLM converts the raw text into a structured contact fields in JSON format.\n\n4. Search API (via SerpAPI) enriches with profile/company info\n\n\u2728 Adjust the prompt here to match your business needs.\n\n5. Typhoon LLM (Email Draft) \u2013 Generates a short greeting email (in case needed)\n\n6. Google Sheets \u2013 Saves the structured contact information.\n\n7. Optional Email Step \u2013 If you\u2019d like to send the greeting email:\n- Connect the Text to JSON and Gmail nodes.\n- Leave them disconnected if you don\u2019t need this feature."
      },
      "typeVersion": 1
    },
    {
      "id": "6f7bac8e-9a60-4865-af09-dc9b16f20ac0",
      "name": "Search Agent",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        1536,
        480
      ],
      "parameters": {
        "text": "=Given this original JSON {{ $json.text }}, your task is to classify and return the following additional fields in JSON:\n\n1. job_type\nPossible values: Technical, Business, Both\n\n2. job_level\nPossible values: Junior, Senior, Manager/Director, Executive\n\n3. sector\nPossible values:\n- Consulting / System Integrator\n- Academic\n- Startup\n- Enterprise\n- Government\n\n4. profile summary\nwhich is a short description about this person as you search their name on the internet\n\nYou will retrieve all relevant information from the Internet to get as most completed information as possible.\n\nReturn only the JSON object. Do not include any code fences or markdown.",
        "options": {},
        "promptType": "define"
      },
      "notesInFlow": false,
      "retryOnFail": false,
      "typeVersion": 2.1,
      "alwaysOutputData": true
    },
    {
      "id": "23231cbf-c8fe-47b2-8994-0d70e5a2dc45",
      "name": "Add Contact to Google Sheet1",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        2832,
        480
      ],
      "parameters": {
        "columns": {
          "value": {
            "email": "={{ $('Text to JSON2').item.json.contact_obj.email }}",
            "phone": "={{ $('Text to JSON2').item.json.contact_obj.phone }}",
            "sector": "={{ $('Text to JSON2').item.json.contact_obj.sector }}",
            "message": "={{ $('On form submission1').item.json['Message (Optional)'] }}",
            "website": "={{ $('Text to JSON2').item.json.contact_obj.website }}",
            "job_type": "={{ $('Text to JSON2').item.json.contact_obj.job_type }}",
            "job_level": "={{ $('Text to JSON2').item.json.contact_obj.job_level }}",
            "job_title_en": "={{ $('Text to JSON2').item.json.contact_obj.job_title_en }}",
            "job_title_th": "={{ $('Text to JSON2').item.json.contact_obj.job_title_th }}",
            "last_name_en": "={{ $('Text to JSON2').item.json.contact_obj.last_name_en }}",
            "last_name_th": "={{ $('Text to JSON2').item.json.contact_obj.last_name_th }}",
            "nick_name_en": "={{ $('Text to JSON2').item.json.contact_obj.nick_name_en }}",
            "nick_name_th": "={{ $('Text to JSON2').item.json.contact_obj.nick_name_th }}",
            "email content": "={{ $json.contact_obj.body }}",
            "email subject": "={{ $json.contact_obj.subject }}",
            "first_name_en": "={{ $('Text to JSON2').item.json.contact_obj.first_name_en }}",
            "first_name_th": "={{ $('Text to JSON2').item.json.contact_obj.first_name_th }}",
            "organization_en": "={{ $('Text to JSON2').item.json.contact_obj.organization_en }}",
            "organization_th": "={{ $('Text to JSON2').item.json.contact_obj.organization_th }}",
            "profile summary": "={{ $('Text to JSON2').item.json.contact_obj.profile_summary }}"
          },
          "schema": [
            {
              "id": "first_name_en",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "first_name_en",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "last_name_en",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "last_name_en",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "first_name_th",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "first_name_th",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "last_name_th",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "last_name_th",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "nick_name_en",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "nick_name_en",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "nick_name_th",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "nick_name_th",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "email",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "email",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "phone",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "phone",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "job_title_en",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "job_title_en",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "job_title_th",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "job_title_th",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "job_type",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "job_type",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "job_level",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "job_level",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "organization_en",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "organization_en",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "organization_th",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "organization_th",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "sector",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "sector",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "website",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "website",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "profile summary",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "profile summary",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "message",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "message",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "email subject",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "email subject",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "email content",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "email content",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1UleIustQHKLHjImasGFFt3ZPi26Uv7JEEVJ6a39uMwI/edit#gid=0",
          "cachedResultName": "Workshop Template"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1UleIustQHKLHjImasGFFt3ZPi26Uv7JEEVJ6a39uMwI",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1UleIustQHKLHjImasGFFt3ZPi26Uv7JEEVJ6a39uMwI/edit?usp=drivesdk",
          "cachedResultName": "BizCard Contacts"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.6
    },
    {
      "id": "d3737de6-7617-4a8a-b2d2-df59f91bc80f",
      "name": "SerpAPI",
      "type": "@n8n/n8n-nodes-langchain.toolSerpApi",
      "position": [
        1696,
        720
      ],
      "parameters": {
        "options": {}
      },
      "credentials": {
        "serpApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "380daa54-9c54-4fa5-9d5d-18c59feea4bd",
      "name": "Text to JSON2",
      "type": "n8n-nodes-base.set",
      "position": [
        1936,
        480
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "d07ce0e8-330a-49fc-b5a3-20decd56c00d",
              "name": "contact_obj",
              "type": "object",
              "value": "={{ $('Search Agent').item.json.output.replaceAll(\"```json\", \"\").replaceAll(\"```\", \"\").trim().parseJson() }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "852cc251-296e-4308-baf0-a79a7bda1d43",
      "name": "Text to JSON",
      "type": "n8n-nodes-base.set",
      "position": [
        3232,
        80
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "d07ce0e8-330a-49fc-b5a3-20decd56c00d",
              "name": "contact_obj",
              "type": "object",
              "value": "={{ $json.text.replaceAll(\"```json\", \"\").replaceAll(\"```\", \"\").trim().parseJson() }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "9a392744-2e06-4aa4-b512-c750fcce476d",
      "name": "On form submission",
      "type": "n8n-nodes-base.formTrigger",
      "disabled": true,
      "position": [
        320,
        -352
      ],
      "parameters": {
        "options": {},
        "formTitle": "Name card collector",
        "formFields": {
          "values": [
            {
              "fieldType": "file",
              "fieldLabel": "nameCard",
              "multipleFiles": false,
              "requiredField": true,
              "acceptFileTypes": ".jpg, .png, .jpeg"
            },
            {
              "fieldType": "textarea",
              "fieldLabel": "Message (Optional)"
            }
          ]
        },
        "formDescription": "Upload your name card and message (optional) here. We will get back to you soon."
      },
      "typeVersion": 2.2
    },
    {
      "id": "09d18c2d-2e76-4f40-b3f0-450b409fd4b6",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -560,
        -160
      ],
      "parameters": {
        "width": 624,
        "height": 672,
        "content": "\n# OCR Business Card Reader with Thai Support \u2013 Enrich & Save Contacts to CRM\n\nThis workflow demonstrates how to use Typhoon OCR + LLM to digitize business cards, enrich the details, and save them directly into Google Sheets (or any CRM). It works with Thai and English name cards and even includes an optional greeting email step.\n\nTwo versions are included:\n\n\ud83d\udfe2 Without Search API \u2192 Cost-free, Typhoon-only enrichment\n\n\ud83d\udd35 With Search API \u2192 Adds Google Search enrichment for richer profiles (may incur cost)\n\nUse cases: Event lead capture, sales enablement, networking database.\n\nPowered by Typhoon \u2192 [opentyphoon.ai](https://opentyphoon.ai) (free API available)\n\n\ud83d\udc49 For step-by-step Typhoon setup, see the [official documentation](https://opentyphoon.ai/blog/en/n8n-typhoon-integration-guide)"
      },
      "typeVersion": 1
    },
    {
      "id": "a6b92d4b-777c-4710-88d1-d9d046225b41",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3120,
        -128
      ],
      "parameters": {
        "color": 7,
        "width": 544,
        "height": 544,
        "content": "Optional nodes to facilitate sending emails"
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "2a769238-0ab3-4d65-9562-e42963b9bcb8",
  "connections": {
    "SerpAPI": {
      "ai_tool": [
        [
          {
            "node": "Search Agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "OCR Agent": {
      "main": [
        [
          {
            "node": "Contact Info Parser",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Edit Image": {
      "main": [
        [
          {
            "node": "OCR Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OCR Agent1": {
      "main": [
        [
          {
            "node": "Contact Info Parser1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Edit Image1": {
      "main": [
        [
          {
            "node": "OCR Agent1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Typhoon OCR": {
      "ai_languageModel": [
        [
          {
            "node": "OCR Agent",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Search Agent": {
      "main": [
        [
          {
            "node": "Text to JSON2",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Text to JSON": {
      "main": [
        []
      ]
    },
    "Typhoon OCR1": {
      "ai_languageModel": [
        [
          {
            "node": "OCR Agent1",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Text to JSON ": {
      "main": [
        [
          {
            "node": "Add Contact to Google Sheet1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Text to JSON2": {
      "main": [
        [
          {
            "node": "Greeting Email Composer",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Text to JSON 2": {
      "main": [
        [
          {
            "node": "Add Contact to Google Sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "On form submission": {
      "main": [
        [
          {
            "node": "Edit Image",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Typhoon 2.1 Gemma1": {
      "ai_languageModel": [
        [
          {
            "node": "Contact Info Parser",
            "type": "ai_languageModel",
            "index": 0
          },
          {
            "node": "Contact Info Enricher",
            "type": "ai_languageModel",
            "index": 0
          },
          {
            "node": "Greeting Email Composer1",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Typhoon 2.1 Gemma2": {
      "ai_languageModel": [
        [
          {
            "node": "Contact Info Parser1",
            "type": "ai_languageModel",
            "index": 0
          },
          {
            "node": "Greeting Email Composer",
            "type": "ai_languageModel",
            "index": 0
          },
          {
            "node": "Search Agent",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Contact Info Parser": {
      "main": [
        [
          {
            "node": "Contact Info Enricher",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "On form submission1": {
      "main": [
        [
          {
            "node": "Edit Image1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Contact Info Parser1": {
      "main": [
        [
          {
            "node": "Search Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Contact Info Enricher": {
      "main": [
        [
          {
            "node": "Greeting Email Composer1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Greeting Email Composer": {
      "main": [
        [
          {
            "node": "Text to JSON ",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Greeting Email Composer1": {
      "main": [
        [
          {
            "node": "Text to JSON 2",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Add Contact to Google Sheet": {
      "main": [
        []
      ]
    },
    "Add Contact to Google Sheet1": {
      "main": [
        []
      ]
    }
  }
}