{
  "name": "GP intake and session matching (GoHighLevel)",
  "nodes": [
    {
      "parameters": {
        "content": "Automation 1: a GP fills your short form, gets checked against the imported GP list, then lands in GoHighLevel tagged with their area and sessions, on the evergreen email sequence.\n\nMVP, not connected yet",
        "height": 130,
        "width": 400
      },
      "name": "What automation 1 does",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [180, 40]
    },
    {
      "parameters": {
        "content": "Automation 2: every weekday morning the new sessions the practices have added are matched to GPs on area and sessions per week, and each GP is emailed only the sessions that fit.\n\nMVP, not connected yet",
        "height": 130,
        "width": 400
      },
      "name": "What automation 2 does",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [180, 560]
    },
    {
      "parameters": {
        "formTitle": "Register for GP sessions",
        "formDescription": "Tell us where you can work and how many sessions a week you want.",
        "formFields": {
          "values": [
            {"fieldLabel": "Full name", "requiredField": true},
            {"fieldLabel": "GMC number", "requiredField": true},
            {"fieldLabel": "Email", "fieldType": "email", "requiredField": true},
            {"fieldLabel": "Mobile", "requiredField": true},
            {"fieldLabel": "Home postcode", "requiredField": true},
            {"fieldLabel": "Sessions per week", "fieldType": "dropdown", "fieldOptions": {"values": [{"option": "1"}, {"option": "2"}, {"option": "3"}, {"option": "4"}, {"option": "5 or more"}]}, "requiredField": true},
            {"fieldLabel": "Travel distance in miles", "fieldType": "dropdown", "fieldOptions": {"values": [{"option": "5"}, {"option": "10"}, {"option": "20"}, {"option": "30 plus"}]}, "requiredField": true}
          ]
        },
        "options": {}
      },
      "name": "GP fills the short form",
      "type": "n8n-nodes-base.formTrigger",
      "typeVersion": 2.2,
      "position": [0, 300],
      "webhookId": "gp-session-signup"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {"id": "a1", "name": "gmcNumber", "value": "={{ $json['GMC number'].toString().replace(/\\D/g, '') }}", "type": "string"},
            {"id": "a2", "name": "email", "value": "={{ $json.Email.trim().toLowerCase() }}", "type": "string"},
            {"id": "a3", "name": "fullName", "value": "={{ $json['Full name'].trim() }}", "type": "string"},
            {"id": "a4", "name": "mobile", "value": "={{ $json.Mobile.toString().replace(/[^0-9+]/g, '') }}", "type": "string"},
            {"id": "a5", "name": "postcode", "value": "={{ $json['Home postcode'].toUpperCase().replace(/\\s+/g, '') }}", "type": "string"},
            {"id": "a6", "name": "outwardCode", "value": "={{ $json['Home postcode'].toUpperCase().replace(/\\s+/g, '').slice(0, -3) }}", "type": "string"},
            {"id": "a7", "name": "sessionsPerWeek", "value": "={{ parseInt($json['Sessions per week'], 10) }}", "type": "number"},
            {"id": "a8", "name": "travelMiles", "value": "={{ parseInt($json['Travel distance in miles'], 10) }}", "type": "number"}
          ]
        },
        "options": {}
      },
      "name": "Tidy name, email, postcode",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [280, 300]
    },
    {
      "parameters": {
        "documentId": {"__rl": true, "value": "GP_LIST_SHEET_ID", "mode": "id"},
        "sheetName": {"__rl": true, "value": "GP list", "mode": "name"},
        "filtersUI": {"values": [{"lookupColumn": "GMC number", "lookupValue": "={{ $json.gmcNumber }}"}]},
        "options": {}
      },
      "name": "Look the GMC number up",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.5,
      "position": [560, 300],
      "alwaysOutputData": true
    },
    {
      "parameters": {
        "conditions": {
          "options": {"caseSensitive": false, "version": 2, "typeValidation": "loose"},
          "combinator": "and",
          "conditions": [
            {"id": "c1", "operator": {"type": "string", "operation": "notEmpty", "singleValue": true}, "leftValue": "={{ $json['GMC number'] }}", "rightValue": ""}
          ]
        },
        "options": {}
      },
      "name": "Already on the GP list?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [840, 300]
    },
    {
      "parameters": {
        "operation": "append",
        "documentId": {"__rl": true, "value": "GP_LIST_SHEET_ID", "mode": "id"},
        "sheetName": {"__rl": true, "value": "Duplicates to check", "mode": "name"},
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "GMC number": "={{ $('Tidy name, email, postcode').item.json.gmcNumber }}",
            "Name": "={{ $('Tidy name, email, postcode').item.json.fullName }}",
            "Email on the form": "={{ $('Tidy name, email, postcode').item.json.email }}",
            "Email already on the list": "={{ $json.Email }}",
            "Submitted": "={{ $now.format('yyyy-LL-dd HH:mm') }}"
          }
        },
        "options": {}
      },
      "name": "Park the duplicate for review",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.5,
      "position": [1120, 160]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://services.leadconnectorhq.com/contacts/upsert",
        "sendHeaders": true,
        "headerParameters": {"parameters": [{"name": "Version", "value": "2021-07-28"}, {"name": "Content-Type", "value": "application/json"}]},
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify({ locationId: 'YOUR_GHL_LOCATION_ID', firstName: $('Tidy name, email, postcode').item.json.fullName.split(' ')[0], lastName: $('Tidy name, email, postcode').item.json.fullName.split(' ').slice(1).join(' '), email: $('Tidy name, email, postcode').item.json.email, phone: $('Tidy name, email, postcode').item.json.mobile, postalCode: $('Tidy name, email, postcode').item.json.postcode, country: 'GB', source: 'GP session form', tags: ['gp', 'area-' + $('Tidy name, email, postcode').item.json.outwardCode.toLowerCase(), $('Tidy name, email, postcode').item.json.sessionsPerWeek + '-sessions'], customFields: [{ key: 'gmc_number', field_value: $('Tidy name, email, postcode').item.json.gmcNumber }, { key: 'sessions_per_week', field_value: $('Tidy name, email, postcode').item.json.sessionsPerWeek }, { key: 'travel_miles', field_value: $('Tidy name, email, postcode').item.json.travelMiles }] }) }}",
        "options": {}
      },
      "name": "Create the GP in GoHighLevel",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [1120, 440]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "=https://services.leadconnectorhq.com/contacts/{{ $json.contact.id }}/workflow/YOUR_EVERGREEN_WORKFLOW_ID",
        "sendHeaders": true,
        "headerParameters": {"parameters": [{"name": "Version", "value": "2021-07-28"}, {"name": "Content-Type", "value": "application/json"}]},
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify({ eventStartTime: $now.toISO() }) }}",
        "options": {}
      },
      "name": "Start the evergreen emails",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [1400, 440]
    },
    {
      "parameters": {
        "rule": {"interval": [{"field": "cronExpression", "expression": "0 7 * * 1-5"}]}
      },
      "name": "Every weekday at 07:00",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.2,
      "position": [0, 780]
    },
    {
      "parameters": {
        "documentId": {"__rl": true, "value": "SESSIONS_SHEET_ID", "mode": "id"},
        "sheetName": {"__rl": true, "value": "Open sessions", "mode": "name"},
        "filtersUI": {"values": [{"lookupColumn": "Status", "lookupValue": "Unfilled"}]},
        "options": {}
      },
      "name": "Read the new practice sessions",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.5,
      "position": [280, 780]
    },
    {
      "parameters": {
        "documentId": {"__rl": true, "value": "GP_LIST_SHEET_ID", "mode": "id"},
        "sheetName": {"__rl": true, "value": "GP list", "mode": "name"},
        "options": {}
      },
      "name": "Read the whole GP list",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.5,
      "position": [560, 780]
    },
    {
      "parameters": {
        "jsCode": "// Match every unfilled session to the GPs who cover that area and still have sessions free.\nconst sessions = $('Read the new practice sessions').all().map(i => i.json);\nconst gps = $input.all().map(i => i.json);\n\nconst outward = pc => (pc || '').toUpperCase().replace(/\\s+/g, '').slice(0, -3);\nconst area = pc => outward(pc).replace(/[0-9].*$/, '');\n\nconst out = [];\n\nfor (const gp of gps) {\n  if ((gp['Status'] || '').toLowerCase() === 'paused') continue;\n\n  const wanted = parseInt(gp['Sessions per week'], 10) || 0;\n  const booked = parseInt(gp['Sessions booked this week'], 10) || 0;\n  const spare = wanted - booked;\n  if (spare < 1) continue;\n\n  const gpArea = area(gp['Home postcode']);\n\n  const fits = sessions.filter(s =>\n    area(s['Practice postcode']) === gpArea &&\n    (parseInt(s['Sessions needed'], 10) || 1) <= spare\n  );\n\n  if (!fits.length) continue;\n\n  out.push({\n    json: {\n      contactId: gp['GoHighLevel contact id'],\n      email: (gp['Email'] || '').trim().toLowerCase(),\n      firstName: (gp['Full name'] || '').split(' ')[0],\n      area: gpArea,\n      spareSessions: spare,\n      matchCount: fits.length,\n      matches: fits.map(s => ({\n        practice: s['Practice name'],\n        postcode: s['Practice postcode'],\n        date: s['Session date'],\n        slot: s['Morning or afternoon'],\n        rate: s['Rate per session']\n      }))\n    }\n  });\n}\n\nreturn out;"
      },
      "name": "Match on area and sessions",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [840, 780]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://services.leadconnectorhq.com/conversations/messages",
        "sendHeaders": true,
        "headerParameters": {"parameters": [{"name": "Version", "value": "2021-04-15"}, {"name": "Content-Type", "value": "application/json"}]},
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify({ type: 'Email', contactId: $json.contactId, subject: $json.matchCount + ' session' + ($json.matchCount > 1 ? 's' : '') + ' near ' + $json.area, html: '<p>Hi ' + $json.firstName + ',</p><p>These came in this morning and fit the ' + $json.spareSessions + ' session(s) a week you are free for:</p><ul>' + $json.matches.map(m => '<li>' + m.practice + ', ' + m.postcode + ' — ' + m.date + ' ' + m.slot + ' at ' + m.rate + '</li>').join('') + '</ul><p>Reply to this email with the ones you want and the desk will confirm with the practice.</p>', emailFrom: 'sessions@YOUR_SENDING_DOMAIN' }) }}",
        "options": {}
      },
      "name": "Email the GP their matches",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [1120, 780]
    }
  ],
  "connections": {
    "GP fills the short form": {"main": [[{"node": "Tidy name, email, postcode", "type": "main", "index": 0}]]},
    "Tidy name, email, postcode": {"main": [[{"node": "Look the GMC number up", "type": "main", "index": 0}]]},
    "Look the GMC number up": {"main": [[{"node": "Already on the GP list?", "type": "main", "index": 0}]]},
    "Already on the GP list?": {"main": [[{"node": "Park the duplicate for review", "type": "main", "index": 0}], [{"node": "Create the GP in GoHighLevel", "type": "main", "index": 0}]]},
    "Create the GP in GoHighLevel": {"main": [[{"node": "Start the evergreen emails", "type": "main", "index": 0}]]},
    "Every weekday at 07:00": {"main": [[{"node": "Read the new practice sessions", "type": "main", "index": 0}]]},
    "Read the new practice sessions": {"main": [[{"node": "Read the whole GP list", "type": "main", "index": 0}]]},
    "Read the whole GP list": {"main": [[{"node": "Match on area and sessions", "type": "main", "index": 0}]]},
    "Match on area and sessions": {"main": [[{"node": "Email the GP their matches", "type": "main", "index": 0}]]}
  },
  "settings": {"executionOrder": "v1"}
}
