{
  "name": "QRlytics Marketing Campaign A/B Testing",
  "nodes": [
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "qrlytics-campaign-scan",
        "responseMode": "responseNode",
        "options": {}
      },
      "id": "webhook-1",
      "name": "Campaign Scan Webhook",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2,
      "position": [240, 300],
      "webhookId": "qrlytics-campaign"
    },
    {
      "parameters": {
        "jsCode": "// Parse QRlytics webhook for marketing campaign\nconst scan = $input.first().json;\nconst now = new Date(scan.scanned_at || new Date().toISOString());\n\n// Extract campaign info from QR code name\n// Expected format: \"CAMPAIGN-VARIANT\" e.g., \"PROMO2026-A\", \"ELECTION-B\"\nconst qrName = scan.qr_code_name || 'CAMPAIGN-A';\nconst parts = qrName.split('-');\nconst campaignId = parts.slice(0, -1).join('-') || 'CAMPAIGN';\nconst variant = parts[parts.length - 1] || 'A';\n\nreturn [{\n  json: {\n    campaign_id: campaignId,\n    variant: variant,\n    qr_code_id: scan.qr_code_id,\n    qr_code_name: qrName,\n    scanned_at: now.toISOString(),\n    date: now.toISOString().split('T')[0],\n    hour: now.getHours(),\n    day_of_week: now.toLocaleDateString('en-US', { weekday: 'long' }),\n    location_city: scan.location?.city || 'Unknown',\n    location_country: scan.location?.country || 'Unknown',\n    device: scan.device?.type || 'Unknown',\n    is_unique: scan.is_unique || false\n  }\n}];"
      },
      "id": "code-1",
      "name": "Parse Campaign Scan",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [460, 300]
    },
    {
      "parameters": {
        "operation": "create",
        "baseId": "YOUR_AIRTABLE_BASE_ID",
        "tableId": "YOUR_CAMPAIGN_SCANS_TABLE_ID",
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "Campaign ID": "={{ $json.campaign_id }}",
            "Variant": "={{ $json.variant }}",
            "QR Code ID": "={{ $json.qr_code_id }}",
            "Scanned At": "={{ $json.scanned_at }}",
            "Date": "={{ $json.date }}",
            "Hour": "={{ $json.hour }}",
            "Day": "={{ $json.day_of_week }}",
            "City": "={{ $json.location_city }}",
            "Country": "={{ $json.location_country }}",
            "Device": "={{ $json.device }}",
            "Is Unique": "={{ $json.is_unique }}"
          }
        }
      },
      "id": "airtable-1",
      "name": "Log to Airtable",
      "type": "n8n-nodes-base.airtable",
      "typeVersion": 2,
      "position": [680, 300],
      "credentials": {
        "airtableTokenApi": {
          "id": "YOUR_CREDENTIAL_ID",
          "name": "Airtable"
        }
      }
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ JSON.stringify({ success: true, campaign: $('Parse Campaign Scan').item.json.campaign_id, variant: $('Parse Campaign Scan').item.json.variant }) }}",
        "options": {}
      },
      "id": "respond-1",
      "name": "Respond Success",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.1,
      "position": [900, 300]
    },
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "days",
              "triggerAtHour": 18
            }
          ]
        }
      },
      "id": "schedule-1",
      "name": "Daily A/B Report Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.2,
      "position": [240, 520]
    },
    {
      "parameters": {
        "operation": "search",
        "baseId": "YOUR_AIRTABLE_BASE_ID",
        "tableId": "YOUR_CAMPAIGN_SCANS_TABLE_ID",
        "filterByFormula": "=IS_SAME({Date}, TODAY(), 'day')",
        "options": {}
      },
      "id": "airtable-2",
      "name": "Get Today's Scans",
      "type": "n8n-nodes-base.airtable",
      "typeVersion": 2,
      "position": [460, 520],
      "credentials": {
        "airtableTokenApi": {
          "id": "YOUR_CREDENTIAL_ID",
          "name": "Airtable"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "// Generate A/B test comparison report\nconst items = $input.all();\nconst today = new Date().toISOString().split('T')[0];\n\n// Group by campaign and variant\nconst campaigns = {};\n\nitems.forEach(item => {\n  const campaignId = item.json.fields?.['Campaign ID'] || item.json['Campaign ID'];\n  const variant = item.json.fields?.['Variant'] || item.json['Variant'];\n  const isUnique = item.json.fields?.['Is Unique'] || item.json['Is Unique'];\n  \n  if (!campaigns[campaignId]) {\n    campaigns[campaignId] = {};\n  }\n  if (!campaigns[campaignId][variant]) {\n    campaigns[campaignId][variant] = { total: 0, unique: 0 };\n  }\n  \n  campaigns[campaignId][variant].total++;\n  if (isUnique === true || isUnique === 'true') {\n    campaigns[campaignId][variant].unique++;\n  }\n});\n\n// Calculate winners for each campaign\nconst results = Object.entries(campaigns).map(([campaignId, variants]) => {\n  const variantData = Object.entries(variants).map(([v, data]) => ({\n    variant: v,\n    ...data\n  }));\n  \n  // Sort by total scans descending\n  variantData.sort((a, b) => b.total - a.total);\n  \n  const winner = variantData[0];\n  const runnerUp = variantData[1];\n  \n  let improvement = 0;\n  if (runnerUp && runnerUp.total > 0) {\n    improvement = Math.round(((winner.total - runnerUp.total) / runnerUp.total) * 100);\n  }\n  \n  return {\n    campaign_id: campaignId,\n    variants: variantData,\n    winner: winner.variant,\n    winner_scans: winner.total,\n    improvement_percent: improvement\n  };\n});\n\nreturn [{\n  json: {\n    date: today,\n    total_scans: items.length,\n    campaigns: results,\n    summary: results.map(r => `${r.campaign_id}: Variant ${r.winner} wins (+${r.improvement_percent}%)`).join('\\n')\n  }\n}];"
      },
      "id": "code-2",
      "name": "Calculate A/B Results",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [680, 520]
    },
    {
      "parameters": {
        "select": "channel",
        "channelId": {
          "__rl": true,
          "value": "YOUR_MARKETING_CHANNEL_ID",
          "mode": "id"
        },
        "text": "=:chart_with_upwards_trend: *Daily A/B Test Report - {{ $json.date }}*\n\n:bar_chart: *Total Scans:* {{ $json.total_scans }}\n\n:trophy: *Results:*\n{{ $json.summary }}\n\n_Full data in Airtable. Generated by QRlytics._",
        "otherOptions": {}
      },
      "id": "slack-1",
      "name": "Send A/B Report",
      "type": "n8n-nodes-base.slack",
      "typeVersion": 2.2,
      "position": [900, 520],
      "credentials": {
        "slackApi": {
          "id": "YOUR_CREDENTIAL_ID",
          "name": "Slack"
        }
      }
    }
  ],
  "connections": {
    "Campaign Scan Webhook": {
      "main": [[{ "node": "Parse Campaign Scan", "type": "main", "index": 0 }]]
    },
    "Parse Campaign Scan": {
      "main": [[{ "node": "Log to Airtable", "type": "main", "index": 0 }]]
    },
    "Log to Airtable": {
      "main": [[{ "node": "Respond Success", "type": "main", "index": 0 }]]
    },
    "Daily A/B Report Trigger": {
      "main": [[{ "node": "Get Today's Scans", "type": "main", "index": 0 }]]
    },
    "Get Today's Scans": {
      "main": [[{ "node": "Calculate A/B Results", "type": "main", "index": 0 }]]
    },
    "Calculate A/B Results": {
      "main": [[{ "node": "Send A/B Report", "type": "main", "index": 0 }]]
    }
  },
  "settings": {
    "executionOrder": "v1"
  },
  "meta": {
    "templateCredsSetupCompleted": false,
    "instanceId": "qrlytics-template"
  },
  "tags": [
    { "name": "QRlytics" },
    { "name": "Marketing" },
    { "name": "A/B Testing" }
  ]
}
