Use Airtable as the source of truth for pharmacy landing pages and attribute Calendly welcome-call bookings back to the referring pharmacy, then write those bookings into the consolidated Airtable Patients table.
Referring pharmaciesNameURL slugQR batch codeprocess.env._data/partners.js now reads .env.local / .env locally and process.env in Netlify.Implemented in js/app.js.
Current precedence for Calendly attribution:
utm_source=pharmacy and utm_content, use that utm_content.QR batch code.QR batch code is missing, fall back to the partner slug.Partner CTA markup now includes:
data-partner-slugdata-partner-utm-contentThis is deliberate because:
URL slug is for routingQR batch code is the canonical utm_contentThose are not always identical in Airtable.
Existing browser listener in js/app.js:
calendly.event_scheduledThat listener now also includes utmSource and utmContent in the Rudder event when available.
Added:
Configured in:
Function endpoint:
/api/calendly-webhook/.netlify/functions/calendly-webhookWhat it does:
POSTCALENDLY_WEBHOOK_SIGNING_KEY is presentinvitee.created eventsReferring pharmacies by QR batch codePatientsoutbound_call location if
CALENDLY_PAT is available in Netlify and the invitee payload does not contain a phoneDefault lead behavior:
LeadNeeds action is set to true on create onlyWelcome callWelcome call bookedPatients.npm run patients:sync-bp.Patients now has a BP patient ID field.Record category = BP patient import value.--apply before it writes to Airtable.NameEmailPhoneRecord categoryBP patient IDAIRTABLE_TOKENAIRTABLE_BASE_IDAIRTABLE_TABLE_NAME=Referring pharmaciesAIRTABLE_VIEWAIRTABLE_TOKENAIRTABLE_BASE_IDAIRTABLE_TABLE_NAME=Referring pharmaciesAIRTABLE_LEADS_TABLE_NAME=PatientsAIRTABLE_LEADS_STAGE_FIELD=Lead stageAIRTABLE_LEADS_SOURCE_FIELD=Lead sourceAIRTABLE_LEADS_CATEGORY_FIELD=Record categoryCALENDLY_PAT if you want phone enrichment from Calendly scheduled eventsCALENDLY_WEBHOOK_SIGNING_KEYAIRTABLE_PARTNERS_QR_FIELDAIRTABLE_PARTNERS_SLUG_FIELDAIRTABLE_LEADS_NAME_FIELDAIRTABLE_LEADS_EMAIL_FIELDAIRTABLE_LEADS_PHONE_FIELDAIRTABLE_LEADS_REFERRING_PHARMACY_FIELDAIRTABLE_LEADS_NOTES_FIELDAIRTABLE_LEADS_SOURCE_FIELDAIRTABLE_LEADS_CATEGORY_FIELDAIRTABLE_LEADS_NEEDS_ACTION_FIELDAIRTABLE_LEADS_INVITEE_URI_FIELDAIRTABLE_LEADS_EVENT_URI_FIELDAIRTABLE_PATIENTS_CATEGORY_FIELD=Record categoryAIRTABLE_PATIENTS_BP_CATEGORY=BP patient importAIRTABLE_PATIENTS_BP_ID_FIELD=BP patient IDQR batch code attribution values.node --check passes for the Netlify webhook function.nathan@azurehealth.com.au.https://deploy-preview-13--hilarious-jalebi-3fa1c2.netlify.app/api/calendly-webhookorganizationinvitee.createdinvitee.created webhook works.Patients is named Referring pharmacy.AIRTABLE_LEADS_REFERRING_PHARMACY_FIELD=Referring pharmacyRecord category = LeadNeeds action = true on createLead source = Welcome callLead stage = Welcome call bookedReferring pharmacylocation.type = outbound_calllocation.location = <phone number>Phone when
CALENDLY_PAT is present in Netlify.CALENDLY_PAT to Netlify and redeploying, phone numbers populated successfully in Airtable.BPS Search Result 2026052750508.csv was imported into Airtable Patients.Updates: 0Creates: 0Already correct: 246Airtable patients: 274Scoped BP patients: 261https://azurehealth.com.au/api/calendly-webhookCALENDLY_WEBHOOK_SIGNING_KEYBook a welcome call from:
Then verify in Airtable Patients:
Record category = LeadNeeds action = trueLead source = Welcome callLead stage = Welcome call bookedReferring pharmacy is blank unless pharmacy attribution is expectedNotes contain the expected UTM valuesCurrent temporary webhook target:
https://deploy-preview-13--hilarious-jalebi-3fa1c2.netlify.app/api/calendly-webhookProduction target should become:
https://azurehealth.com.au/api/calendly-webhookPractical options:
Reason to leave preview in place temporarily:
If you want signed webhook verification in production:
Webhook signing keyCALENDLY_WEBHOOK_SIGNING_KEY in NetlifyUntil then, leaving that env var unset keeps the current unsigned webhook behavior.
Recommended next improvements after webhook testing:
Calendly invitee URI field to PatientsCalendly event URI field to PatientsAIRTABLE_LEADS_INVITEE_URI_FIELD=Calendly invitee URIAIRTABLE_LEADS_EVENT_URI_FIELD=Calendly event URIThis will give clean dedupe on repeated webhook deliveries or reschedules.
The safe BP import intentionally skipped uncertain rows. Use the exception lists from:
npm run patients:sync-bp
Current unresolved buckets after the BPS Search Result 2026052750508.csv import:
Skipped duplicate source patients: 8Skipped duplicate-name creates: 14Existing BP patients not matched by this CSV: 15Work through these manually using the process in scripts/README.md:
BP patient ID manually when the existing Airtable record is clearly the same patientinvitee.canceled as well, so stage/status can update on cancellations or reschedules?CALENDLY_PAT in Netlify long-term for phone enrichment, or change the Calendly form
to expose phone as a custom question instead?Decided:
.env.example should stay committed because it contains placeholders only and documents required config