AutomationFlowsGeneral › Capture Vcard Qr Code Contacts with Allcoderelay and Add Them to Klicktipp

Capture Vcard Qr Code Contacts with Allcoderelay and Add Them to Klicktipp

ByKlickTipp @KlickTipp on n8n.io

Community Node Disclaimer: This workflow uses KlickTipp community nodes.

Webhook trigger★★★★☆ complexity9 nodesN8N Nodes Klicktipp
General Trigger: Webhook Nodes: 9 Complexity: ★★★★☆ Added:

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

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
{
  "id": "G5K8zclSBhyHKgrPEox1M",
  "name": "Transfer scanned vCard QR codes data to KlickTipp",
  "tags": [
    {
      "id": "15wrq9sti6wyqr6J",
      "name": "TEMPLATE",
      "createdAt": "2025-01-08T16:34:30.163Z",
      "updatedAt": "2025-01-08T16:34:30.163Z"
    }
  ],
  "nodes": [
    {
      "id": "fe60251e-5a6a-44db-97ea-f7f562cadfc3",
      "name": "Documentation",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1184,
        -352
      ],
      "parameters": {
        "width": 695,
        "height": 1164,
        "content": "Community Node Disclaimer: This workflow uses KlickTipp community nodes.\n\n## Introduction\nThis workflow captures **vCard contact data from a scanned QR code** using **AllCodeRelay** and automatically **creates a contact in KlickTipp**. It is built for event lead capture and fast contact transfer: scan a vCard QR code, send the scan result to a webhook, parse the vCard into clean contact fields, and submit the contact to KlickTipp with **Double Opt-In (DOI)**.\n\nThis template is ideal for marketers, sales teams, and event staff who want a frictionless way to digitize business cards and subscribe contacts into KlickTipp in real time.\n\n## How it works\n1. **Trigger: Scan Webhook from AllCodeRelay**\n   - AllCodeRelay sends the scan payload to an n8n webhook endpoint.\n2. **Filter: vCard Only**\n   - Only payloads containing a valid vCard are processed (e.g., `BEGIN:VCARD ... END:VCARD`).\n3. **Parse: vCard \u2192 Contact Fields**\n   - Extracts and normalizes these fields for mapping:\n     - `firstName`, `lastName`, `email`, `mobile`, `fax`, `phone`\n     - `company`, `position`\n     - `street`, `city`, `state`, `zip`, `country`\n     - `website`\n4. **KlickTipp: Add Contact with DOI**\n   - Creates or updates the subscriber and triggers DOI.\n\n## Setup Instructions\n1. **AllCodeRelay configuration**\n   - Install AllCodeRelay on your phone.\n   - Create a webhook destination pointing to the n8n webhook URL from Step 2.\n   - Scan a **static vCard QR code**.\n\n2. **n8n configuration**\n   - Open the first node (Webhook) and copy the **Test URL** (or Production URL after publishing).\n   - In AllCodeRelay, paste the URL as the webhook target.\n   - Run a test scan and verify that the workflow receives `BEGIN:VCARD` payloads.\n\n3. **KlickTipp configuration**\n   - Create a segmentation **tag** (e.g., `vCard QR code scan`)\n   - Authenticate your KlickTipp connection with **username/password** credentials (API access required).\n   - Map the parsed fields to the **\"KlickTipp: Add Contact with DOI\"** node.\n   - Select DOI opt-in process and tag **\"vCard QR code scan\"**.\n\n## Notes\n- This template supports **vCard QR codes** where the QR content is the vCard text itself (static/offline vCard).\n- If your QR code resolves to a short URL (dynamic QR), the scan will only return the URL and will not contain embedded vCard data.\n- Extend parsing to support additional vCard fields if needed (e.g., multiple emails, additional addresses).\n"
      },
      "typeVersion": 1
    },
    {
      "id": "ff10f325-29df-41b6-9344-f8dced91f5c6",
      "name": "3. Parse data",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -32,
        -352
      ],
      "parameters": {
        "color": 7,
        "width": 216,
        "height": 364,
        "content": "## 3. Parse data"
      },
      "typeVersion": 1
    },
    {
      "id": "6a51df39-d4a8-4b78-8947-96b1c496af10",
      "name": "4. Save data",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        192,
        -352
      ],
      "parameters": {
        "color": 7,
        "width": 248,
        "height": 364,
        "content": "## 4. Save data"
      },
      "typeVersion": 1
    },
    {
      "id": "b56bab82-0950-4744-b4bb-f5d0d7abfa0f",
      "name": "2. Filter data",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -240,
        -352
      ],
      "parameters": {
        "color": 7,
        "width": 200,
        "height": 364,
        "content": "## 2. Filter data"
      },
      "typeVersion": 1
    },
    {
      "id": "6fbefa0f-9d6e-4a8f-803e-6e81e7c8318a",
      "name": "1. Get data",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -480,
        -352
      ],
      "parameters": {
        "color": 7,
        "width": 232,
        "height": 364,
        "content": "## 1. Get data"
      },
      "typeVersion": 1
    },
    {
      "id": "805e3da5-52e2-48a6-9458-e7a6d2a674e7",
      "name": "Filter: vCard Only",
      "type": "n8n-nodes-base.filter",
      "position": [
        -192,
        -208
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "1bebea66-1800-4d98-b610-9caaf215b579",
              "operator": {
                "type": "string",
                "operation": "startsWith"
              },
              "leftValue": "={{ $json.body.code }}",
              "rightValue": "BEGIN:VCARD"
            },
            {
              "id": "58da7eab-7eb8-4f0c-84e1-cded0e704bf6",
              "operator": {
                "type": "string",
                "operation": "endsWith"
              },
              "leftValue": "={{ $json.body.code }}",
              "rightValue": "END:VCARD"
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "ec1aa8d3-2523-469c-b9e1-74cc182d13e7",
      "name": "Parse: vCard \u2192 Contact Fields",
      "type": "n8n-nodes-base.code",
      "position": [
        32,
        -208
      ],
      "parameters": {
        "jsCode": "const items = $input.all();\n\nconst normalize = (s) =>\n  String(s ?? \"\")\n    .replace(/\\r\\n/g, \"\\n\")\n    .replace(/\\r/g, \"\\n\")\n    .trim();\n\n// vCard folded lines: newline + space/tab continues previous line\nconst unfold = (s) => s.replace(/\\n[ \\t]/g, \"\");\n\nconst pickFirst = (arr) => (arr && arr.length ? arr[0].value : \"\");\n\nconst hasParam = (entry, param) =>\n  (entry.params || []).some((p) => String(p).toUpperCase() === param);\n\nconst pickTel = (telEntries, wantedParam) => {\n  if (!telEntries?.length) return \"\";\n  const found = telEntries.find((e) => hasParam(e, wantedParam));\n  return found?.value || \"\";\n};\n\nconst pickEmail = (emailEntries) => {\n  if (!emailEntries?.length) return \"\";\n  // prefer WORK if present, else first\n  const work = emailEntries.find((e) => hasParam(e, \"WORK\"));\n  return (work || emailEntries[0]).value || \"\";\n};\n\nreturn items.map((item) => {\n  const body = item.json.body;\n\n  // Most common shape (your screenshot): body is object and vCard is in body.code\n  const vcardRaw =\n    (typeof body === \"object\" && body?.code) ? body.code :\n    (typeof body === \"string\") ? body :\n    item.json.code || \"\";\n\n  const vcard = unfold(normalize(vcardRaw));\n  if (!vcard.startsWith(\"BEGIN:VCARD\")) throw new Error(\"No vCard found in payload\");\n\n  // Parse into: { KEY: [ { params:[], value:\"...\" }, ... ] }\n  const parsed = {};\n  for (const line of vcard.split(\"\\n\")) {\n    if (!line || line.startsWith(\"BEGIN:\") || line.startsWith(\"END:\") || line.startsWith(\"VERSION:\")) continue;\n\n    const idx = line.indexOf(\":\");\n    if (idx === -1) continue;\n\n    const left = line.slice(0, idx);\n    const value = line.slice(idx + 1).trim();\n\n    const parts = left.split(\";\");\n    const key = parts[0].toUpperCase();\n    const params = parts.slice(1).map((p) => String(p).toUpperCase());\n\n    if (!parsed[key]) parsed[key] = [];\n    parsed[key].push({ params, value });\n  }\n\n  // Name\n  let firstName = \"\";\n  let lastName = \"\";\n\n  const nVal = pickFirst(parsed.N); // \"Last;First;...\"\n  if (nVal) {\n    const [ln = \"\", fn = \"\"] = nVal.split(\";\");\n    firstName = fn;\n    lastName = ln;\n  } else {\n    // fallback: split FN\n    const fn = pickFirst(parsed.FN).trim();\n    if (fn) {\n      const seg = fn.split(/\\s+/);\n      if (seg.length >= 2) {\n        lastName = seg.pop();\n        firstName = seg.join(\" \");\n      } else {\n        firstName = fn;\n      }\n    }\n  }\n\n  // Address (ADR: POBOX;EXT;STREET;CITY;REGION;POSTAL;COUNTRY)\n  const adrVal = pickFirst(parsed.ADR);\n  let street = \"\", city = \"\", state = \"\", zip = \"\", country = \"\";\n  if (adrVal) {\n    const adrParts = adrVal.split(\";\");\n    street = adrParts[2] || \"\";\n    city   = adrParts[3] || \"\";\n    state  = adrParts[4] || \"\";\n    zip    = adrParts[5] || \"\";\n    country= adrParts[6] || \"\";\n  }\n\n  // Phones\n  const tel = parsed.TEL || [];\n  const mobile = pickTel(tel, \"CELL\") || pickTel(tel, \"MOBILE\");\n  const fax = pickTel(tel, \"FAX\");\n  const phone = pickTel(tel, \"WORK\") || pickTel(tel, \"VOICE\") || (tel[0]?.value || \"\");\n\n  // Email / Company / Position / Website\n  const email = pickEmail(parsed.EMAIL);\n  const company = pickFirst(parsed.ORG);\n  const position = pickFirst(parsed.TITLE);\n  const website = pickFirst(parsed.URL);\n\n  return {\n    json: {\n      firstName,\n      lastName,\n      email,\n      mobile,\n      fax,\n      phone,\n      company,\n      position,\n      street,\n      city,\n      state,\n      country,\n      zip,\n      website,\n    },\n  };\n});"
      },
      "typeVersion": 2
    },
    {
      "id": "15e80a2a-dd05-4d7b-a374-aeef1bcfa6a4",
      "name": "AllCodeRelay: Scan Webhook",
      "type": "n8n-nodes-base.webhook",
      "position": [
        -416,
        -208
      ],
      "parameters": {
        "path": "qr-code-scanner",
        "options": {},
        "httpMethod": "POST"
      },
      "typeVersion": 2.1
    },
    {
      "id": "f2ed5fa0-276d-473c-9de1-9feba4679e9d",
      "name": "KlickTipp: Add Contact with DOI",
      "type": "n8n-nodes-klicktipp.klicktipp",
      "position": [
        256,
        -208
      ],
      "parameters": {
        "email": "={{ $json.email }}",
        "tagId": "14145915",
        "fields": {
          "dataFields": [
            {
              "fieldId": "fieldFirstName",
              "fieldValue": "={{ $json.firstName }}"
            },
            {
              "fieldId": "fieldLastName",
              "fieldValue": "={{ $json.lastName }}"
            },
            {
              "fieldId": "fieldCompanyName",
              "fieldValue": "={{ $json.company }}"
            },
            {
              "fieldId": "fieldPhone",
              "fieldValue": "={{ $json.phone }}"
            },
            {
              "fieldId": "fieldMobilePhone",
              "fieldValue": "={{ $json.mobile }}"
            },
            {
              "fieldId": "fieldFax",
              "fieldValue": "={{ $json.fax }}"
            },
            {
              "fieldId": "fieldWebsite",
              "fieldValue": "={{ $json.website }}"
            },
            {
              "fieldId": "fieldStreet1",
              "fieldValue": "={{ $json.street }}"
            },
            {
              "fieldId": "fieldCity",
              "fieldValue": "={{ $json.city }}"
            },
            {
              "fieldId": "fieldState",
              "fieldValue": "={{ $json.state }}"
            },
            {
              "fieldId": "fieldCountry",
              "fieldValue": "={{ $json.country }}"
            },
            {
              "fieldId": "fieldZip",
              "fieldValue": "={{ $json.zip }}"
            }
          ]
        },
        "listId": "358895",
        "resource": "subscriber",
        "operation": "subscribe",
        "smsNumber": "={{ $json.phone }}"
      },
      "credentials": {},
      "typeVersion": 3
    }
  ],
  "active": false,
  "settings": {
    "availableInMCP": false,
    "executionOrder": "v1"
  },
  "versionId": "a5c02d70-688a-49bd-baf1-462007049441",
  "connections": {
    "Filter: vCard Only": {
      "main": [
        [
          {
            "node": "Parse: vCard \u2192 Contact Fields",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AllCodeRelay: Scan Webhook": {
      "main": [
        [
          {
            "node": "Filter: vCard Only",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse: vCard \u2192 Contact Fields": {
      "main": [
        [
          {
            "node": "KlickTipp: Add Contact with DOI",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
Pro

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

About this workflow

Community Node Disclaimer: This workflow uses KlickTipp community nodes.

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

More General workflows → · Browse all categories →

Related workflows

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

General

A production-ready authentication workflow implementing secure user registration, login, token verification, and refresh token mechanisms. Perfect for adding authentication to any application without

Crypto, Data Table, Execute Workflow Trigger
General

Portfolio Orchestrator. Uses httpRequest. Webhook trigger; 59 nodes.

HTTP Request
General

This n8n template demonstrates how a simple Multi-Layer Perceptron (MLP) neural network can predict housing prices. The prediction is based on four key features, processed through a three-layer model.

General

github code Try yourself

Google Calendar
General

This workflow contains community nodes that are only compatible with the self-hosted version of n8n.

N8N Nodes 1Shot