π Changelog
All updates and improvements to SimProSync
v1.7.3
25 Feb 2026
π¦ Auto-Create Quote/Job from GHL Project Custom Objects
Latest- β NEW: Auto-create quote or job in SimPro when a Project custom object is created in GHL
- β Reads the Lead/Quote/Job dropdown field on the GHL Project to determine entity type
- β Configurable via new "βοΈ Project Type Field" setting in GHL β SimPro field mappings
- β NEW: Per-integration toggle: "Auto-create quote/job from GHL Project" in Settings
- β NEW: Type change detection β changing Lead/Quote/Job dropdown from Quote to Job (or vice versa) creates the new entity in SimPro
- β Type change detection uses project_sync_links table to compare previous vs current entity type
- β Each type change creates a separate SimPro entity (mirrors SimPro behaviour where quotes and jobs are distinct records)
- β NEW: Dedup protection via project_sync_links lookup β prevents duplicate creation when GHL workflow fires multiple times
- β NEW: Creation lock (same pattern as site dedup) prevents race conditions from near-simultaneous webhooks
- β Stores GHLβSimPro links in new project_sync_links table for bidirectional tracking
- β Writes project_id, quote_id or job_id back to GHL Project custom object after creation
- β Loop prevention via ghl_custom_object_tracker table (5-minute TTL)
- β NEW: "βοΈ SimProSync Settings" group added to SimPro fields dropdown for special configuration fields
- β GHL workflow setup: "SimPROsync Projects Webhook" with Project Created/Changed triggers
v1.7.2
24 Feb 2026
ποΈ Auto-Create Site, Quote-to-Job Conversion & Loop Prevention Fix
- β NEW: Auto-create Site in SimPro when a GHL contact has no existing site
- β Per-integration toggle: "Auto-create site for new customers" in Settings
- β Site creation uses customer name and address with dedup lock protection
- β FIX: Loop prevention for site.created webhooks β site creation from unified webhook no longer triggers echo loop
- β Added simpro_entity_loop_tracker table to track entities created by SimProSync
- β NEW: Quote-to-Job conversion when opportunity moves to a job pipeline stage
- β Pipeline stage mappings with entity_type = "job" now trigger automatic quoteβjob conversion in SimPro
- β Conversion uses SimPro ConvertToJob endpoint with proper error handling
v1.8.1
23 Feb 2026
π‘οΈ Race Condition Fixes, Duplicate Prevention & Context Detection Fix
- β FIX: GHL unified webhook was logging duplicate entries for single events
- β Root cause: PHP script took too long processing (enrichment API calls) causing server-side request retries
- β Added early 200 response β GHL now gets instant acknowledgement BEFORE heavy processing begins
- β Added ignore_user_abort(true) and fastcgi_finish_request() for clean async processing
- β NEW: Request ID debounce system β each webhook generates a unique 32-char ID
- β Atomic INSERT...ON DUPLICATE KEY UPDATE claims the slot, then SELECT verifies ownership
- β Even if two requests hit at the exact same millisecond, only one wins β the other blocks itself
- β Replaces unreliable rowCount() approach which gave inconsistent results with conditional IF() updates
- β unified_webhook_tracker table now has request_id column (auto-migrated)
- β FIX: SimPro individual customer events were bypassing the throttle entirely
- β individual.customer.updated and individual.customer.created now included alongside company variants
- β FIX: SimPro webhook_processor.php had same SELECT-then-INSERT race condition
- β Replaced with atomic upsert using process_count tracking for bulletproof deduplication
- β FIX: Three-layer customer ID resolution in context_detection.php
- β Layer 1: Raw webhook payload field display names (e.g. "Simpro Customer ID")
- β Layer 2: ghl_discovered_fields table lookup (resolves GHL internal field IDs to display names)
- β Layer 3: sync_links table fallback
- β Previously only checked GHL internal field IDs which never matched β causing duplicate customer creation loops
- β Increased GHL unified webhook debounce window from 15s to 30s
- β SQL cleanup script provided to remove 107 duplicate entries from event_logs table
v1.8.0-beta
22 Feb 2026
β‘ Unified Webhook & Full Job Support (v1.8.0-beta)
- β NEW: Unified Webhook Architecture β one webhook URL handles everything!
- β No more separate workflows for customer.upsert, job.create, quote.create etc.
- β Single GHL workflow with 4 triggers (Contact Created/Changed, Opportunity Created/Changed) replaces all individual action webhooks
- β NEW: Pipeline Stage Mappings are now the Decision Engine
- β entity_type column in pipeline_stage_mappings drives whether to create a Quote or Job
- β Webhook automatically detects: create quote, create job, convert quoteβjob, or sync stage β all from pipeline stage context
- β NEW: Full Job Creation support in unified webhook
- β Jobs created directly when pipeline stage entity_type = "job" and no existing links found
- β Applies SimPro stage/status from pipeline mapping automatically
- β NEW: Customer Update support β existing SimPro customers now PATCHED when GHL contact fields change
- β Previously only created new customers β now does full upsert (create if new, update if existing)
- β Includes fallback: tries individuals endpoint first, then companies if that fails (or vice versa)
- β NEW: Quote-to-Job Conversion
- β When opportunity moves to a "job" stage but has an existing quote link, automatically converts: marks quote as Approved β creates new job from quote details
- β Preserves quote site, name, and type during conversion
- β NEW: Dual Entity Stage Sync
- β Stage changes now sync for both quotes AND jobs based on linked_entity_type
- β Updates correct link table (opportunity_quote_links or opportunity_job_links) with new stage
- β NEW: Loop Prevention for unified webhook
- β Sync source check: blocks echo-back if SimPro synced same contact within 15 seconds
- β Rapid-fire debounce: blocks duplicate webhooks for same contact within 3 seconds via unified_webhook_tracker table
- β Race condition protection via ON DUPLICATE KEY on all link table INSERTs
- β NEW: Simplified GHL Mappings Page (replaces old per-action page)
- β Single page with unified webhook URL, setup status sidebar, and customer field mappings
- β Live status showing customer mappings, pipeline stages (quote/job breakdown), associations, and linked entity counts
- β Server-side customer mapping count with AJAX fallback for reliable status display
- β Links to Pipeline Stage Mappings, Association Mappings, and Status Value Mappings pages
- β All existing customer field mappings carry over automatically β no remapping needed
- β Keeps all features: drag-and-drop groups, searchable dropdowns, discovered fields, clone, manual entry
- β Page split into modular includes (ghl_mapping_fields.php, ghl_mapping_js.php) β 1,168 lines vs 2,166 old monolith
- β IMPROVED: GHL Webhook Logs page now fully supports unified events
- β Unified events show proper badges: Success (green), Loop Blocked (blue), Duplicate Blocked (blue), Error (red)
- β Friendly event labels: "unified.customer_update+job_create" β "Unified Customer Update+Job Create"
- β New expandable "Unified Decision Context" section showing the decision engine results
- β Fixed false "Ignored" badge caused by "Loop prevention checks passed" text matching legacy loop detection
- β IMPROVED: Full event_logs logging on success AND error β unified webhook events now always appear in SimProSync logs
- β IMPROVED: opportunity_quote_links table now stores ghl_pipeline_id, ghl_pipeline_stage_id, simpro_quote_stage columns
- β IMPROVED: Reverse sync (SimProβGHL) now reads real pipeline columns from quote links instead of NULL aliases
- β IMPROVED: Production webhook INSERT statements updated to populate new quote link columns
- β Fully backwards compatible β old per-action workflows still work alongside unified webhook
- β Modular codebase: unified webhook split into context_detection.php, action_executor.php, entity_actions.php
- β TESTED: Live production test with Definitive Electrical β customer update + job creation + loop prevention all confirmed working
v1.6.3
10 Feb 2026
π SimPro β GHL Opportunity Reverse Sync (The Final Piece!)
- β CRITICAL FIX: SimPro Quote updates now sync back to GHL Opportunities!
- β getLinkedOpportunity() was only checking opportunity_job_links table - never checked opportunity_quote_links for quotes
- β Function now checks the correct table based on entity type (quotes vs jobs)
- β Added fallback to sync_links table for quotes created before this fix
- β FIX: Quote creation from GHL Opportunities now saves link to opportunity_quote_links table
- β Previously only saved to sync_links - reverse sync couldn't find the link
- β Now saves to BOTH tables for complete coverage
- β THE CIRCLE IS NOW COMPLETE: GHL Opportunity β SimPro Quote β GHL Project (Custom Object)
- β Pipeline stage changes in SimPro now update the linked GHL Opportunity stage
v1.6.2
4 Feb 2026
π Bidirectional Pipeline Stage Sync Fix & Integration ID Stability
- β CRITICAL FIX: Integration IDs no longer regenerate when saving settings!
- β save_settings.php was using DELETE+INSERT pattern which created new auto-increment IDs
- β This broke webhook URLs, field mappings, and pipeline mappings after every settings save
- β Now uses UPDATE for existing records, INSERT only for new ones
- β FIX: GHL β SimPro pipeline stage sync now working for Jobs
- β job_create.php now looks up pipeline_stage_mappings table to set Stage/Status
- β When opportunity moves to "Job Scheduled π", job is created with correct SimPro status
- β FIX: GHL β SimPro pipeline stage sync now working for Quotes
- β quote_create.php now includes pipeline_stage_mappings lookup (was missing entirely)
- β FIX: GHL β SimPro pipeline stage sync now working for Leads
- β lead_create.php now includes pipeline_stage_mappings lookup (was missing entirely)
- β Pipeline stage mapping applied to both CREATE and UPDATE operations
- β Respects sync_direction setting (both, ghl_to_simpro, simpro_to_ghl)
- β Confirmed: Bidirectional sync working - SimPro β GHL pipeline stages stay in sync
v1.6.1
3 Feb 2026
ποΈ Major Code Refactoring, SimPro API Fixes & Google Drive Disconnect Fix
- β REFACTOR: Modularised webhook_ghl.php from 9,712 lines to 3,826 lines!
- β Split 36 helper functions into 7 dedicated modules in core/ghl/
- β Split 11 action handlers into individual files in core/ghl/actions/
- β Main webhook file now acts as a slim router - much easier to maintain
- β Helper modules: discovery.php, simpro_api.php, ghl_api.php, enrichment.php, pipeline.php, payload_builder.php, helpers.php
- β Action modules: customer_upsert.php, site_create.php, job_create.php, quote_create.php, lead_create.php, notes.php, job_create_object.php, quote_create_object.php, lead_create_object.php, site_create_object.php, opportunity_stage_changed.php
- β FIX: Status field in pipeline sync now sends plain integer instead of ['ID' => x] object
- β FIX: Customer field in lead.create now sends plain integer instead of ['ID' => x] object
- β FIX: Type field in orphan lead creation now sends string 'Project' instead of ['ID' => 1] object
- β These fixes resolve silent failures when updating SimPro job statuses and creating leads
- β FIX: Google Drive disconnect now works when admin is managing another user's settings
- β Disconnect link now passes target_user_id parameter
- β google_disconnect.php now respects admin context and disconnects correct user
- β Redirect after disconnect preserves manage_user_id for admins
v1.6.0
2 Feb 2026
π Generic Dropdown Resolver, API Optimisation & Project Type Routing
- β NEW: Generic Dropdown Field Resolver - converts text values to SimPro IDs (and vice versa) for ANY dropdown field
- β Dropdown resolution is configuration-driven via simpro_dropdown_config table - no hardcoding needed!
- β Supports: PaymentTerms, TaxStatus, JobType, CostCentre, PaymentMethods, and any future dropdown field
- β Works bi-directionally: GHLβSimPro (textβID) and SimProβGHL (IDβtext)
- β In-memory caching prevents duplicate API calls when resolving multiple records in same request
- β NEW: API Call Optimisation via change detection hashing
- β SimPro webhooks now skip full enrichment (20-30 API calls) when entity data hasn't changed
- β Uses MD5 hash of key fields (Stage, Status, DateModified, names, etc.) to detect changes
- β Most repeat webhooks now complete in under 1 second instead of 5-6 seconds
- β Hash cache auto-cleans entries older than 24 hours
- β FIX: Enhanced project_type_field detection for Quote/Job/Lead routing
- β Now checks payload, customData block, AND enriched custom object data (was only checking payload)
- β Better debug logging when project_type_field value can't be found - shows available payload keys
- β Clearer setup instructions in logs when __setting_project_type_field is not configured
v1.5.62
19 Jan 2026
π§ Customer Profile & Group Sync Fix (GHL β SimPro)
- β FIX: GHL custom field unique keys now correctly map to webhook payload display names
- β GHL sends "Customer Type" in payload but mappings use "contact.customer_type" unique key
- β Added automatic conversion: contact.customer_type β "Customer Type" for payload lookup
- β Also handles "OR" fields: contact.domestic_or_commercial β "Domestic OR Commercial"
- β Customer Profile and Customer Group now sync correctly from GHL to SimPro
v1.5.61
19 Jan 2026
π± Phone Label Capitalisation Fix (SimPro β GHL)
- β FIX: Additional phone labels now capitalised correctly for GHL API
- β GHL requires "Mobile", "Home", "Work", "Landline" - was sending lowercase "mobile", "home"
- β This was causing HTTP 422 errors and blocking ALL customer updates from SimPro to GHL
- β Customer Profile and Customer Group syncs were also failing due to this error
- β Added ucfirst(strtolower()) to ensure correct capitalisation
v1.5.60
19 Jan 2026
π± Mobile Phone Mapping Fix (GHL β SimPro)
- β FIX: Mobile Phone field now syncs correctly for Individual customers
- β Root cause: contact.CellPhone was being used instead of customer.CellPhone
- β contact.CellPhone is for Company customer Contacts only - Individual customers need customer.CellPhone
- β Updated simpro_fields.php to clarify field labels and prevent future confusion
- β Contact/Employee fields now show "(Company Contacts Only)" suffix in dropdown
- β Customer Phone field now shows "(Primary)" suffix for clarity
- β DATA FIX: Corrected existing bad mappings in ghl_field_mappings table for affected integrations
v1.5.59
19 Jan 2026
π€ Customer Profile Sync (GHL β SimPro)
- β NEW: Customer Profile names are now automatically converted to SimPro IDs
- β When GHL sends "Gold", system looks up the ID (e.g., 11) and sends that to SimPro
- β Added findSimproCustomerProfileId() function - fetches and caches all profiles from SimPro
- β Profiles are fetched from /setup/customerProfiles/ endpoint and cached per request
- β Handles both customer.Profile.CustomerProfile.Name and entity.Profile.CustomerProfile.Name
- β Logs show which profiles are available if a match is not found
v1.5.58
19 Jan 2026
π± Phone Sync Fix (SimPro β GHL)
- β FIX: additionalPhones now uses "phoneLabel" instead of "label" (was causing 422 errors)
- β GHL API requires "phoneLabel" for additional phone numbers - our code was using wrong key
- β FIX: formatUkPhoneNumber now properly preserves + prefix on international numbers
- β Numbers like +441773775411 were being sent as 441773775411 (missing +)
- β Improved UK phone formatting: handles 07, 0, and 44 prefixes correctly
v1.5.57
13 Jan 2026
π Quote & Lead Duplicate Prevention
- β CRITICAL: Added METHOD 0 duplicate check for quote.create (matches job.create logic)
- β CRITICAL: Added METHOD 0 duplicate check for lead.create (was completely missing!)
- β Quotes/Leads now check enriched Contact Projects for existing quote_id/lead_id before creating
- β Added opportunity_quote_links table for tracking OpportunityβQuote associations
- β Added opportunity_lead_links table for tracking OpportunityβLead associations
- β Prevents duplicate quotes/leads when webhooks fire multiple times
v1.5.9
26 Dec 2025
Read-Only Fields & Site Lookup Fix
- β Fixed HTTP 422 "Total.ExTax - This API Column does not allow POST requests" error
- β Now removes read-only Total field from all Job, Quote, and Lead payloads
- β Fixed Status field validation - must be integer ID, not object with Name
- β Improved Site ID lookup for job.create_object - now searches contact custom fields properly
- β Added fallback to fetch customer's primary site from SimPro when Site ID not found
- β Applied fixes to: job.create, job.create_object, quote.create, quote.create_object, lead.create_object
- β Fixed Retry button - was missing action parameter in URL, now properly reports success/failure
v1.5.8
26 Dec 2025
Company vs Individual Customer Payload Fix
- β Fixed HTTP 422 errors when creating Company customers
- β Company customers no longer send GivenName/FamilyName at top level
- β Individual customers no longer send nested "contact" object
- β Fixed cascade customer creation in site.create and job.create
- β Fixed fallback CREATE scenario payload cleanup
v1.5.7
25 Dec 2025
GHL β SimPro Custom Object Create Fix
- β Fixed site.create_object was not looking up Customer ID from associated contact
- β Fixed job.create_object was not looking up Customer ID from associated contact
- β Fixed quote.create_object was not looking up Customer ID from associated contact
- β Fixed lead.create_object was not looking up Customer ID from associated contact
- β All *.create_object handlers now properly call getSimproCustomerIdFromAssociatedContact()
- β Sites, Jobs, Quotes, and Leads can now be created from GHL custom objects when linked to a contact
v1.5.6
25 Dec 2025
Object-to-Object Associations Fix
- β Fixed Sites β Projects associations not being called
- β Association functions are now properly invoked after successful API calls
- β Improved logging for association creation
v1.5.0
20 Dec 2025
Enrichment Engine & Smart Mapping Analysis
- β Added GHL Enrichment Engine - fetches full contact, business, opportunities, and custom objects
- β Smart Mapping Analysis now shows all available fields from enriched data
- β Improved field lookup with unified GHL data
- β Added support for custom object properties in mappings
v1.4.0
13 Dec 2025
Quote Creation, ID Writebacks & Safety Checks
- β Fixed quote creation - Customer now sent as integer, auto-fetches Site/Type/Name
- β Fixed enrichment to use correct contact_id instead of opportunity ID
- β Added Company vs Individual customer auto-detection
- β Improved Contact events handling (separate from Customer events)
- β Added safety check to prevent accidental mapping deletion
- β Fixed Site ID writeback API URL bug
- β Added Job ID writeback for custom objects
- β Added Quote ID writeback for custom objects
- β Added smart fallbacks for site_id, job_id, quote_id field names
- β Removed debug logging code
v1.3.0
10 Dec 2025
Site Update Fixes & Log Improvements
- β Fixed site update failures - removed trailing slashes from PATCH URLs
- β Fixed site.create_object payload structure
- β Improved log display with loop prevention detection
- β Added "No Action" status display
- β Added detailed SimPro API Calls section in logs
v1.2.0
8 Dec 2025
Site ID Writeback & Dynamic Field Mapping
- β Made Site ID writeback fully dynamic using ghl_field_mappings table
- β Supports any custom object name and field name
- β Improved association handling between custom objects and contacts
v1.1.0
1 Dec 2025
Pipeline Stage Mapping
- β Added bi-directional pipeline stage sync
- β SimPro job stages now sync to GHL opportunity stages
- β GHL opportunity stage changes sync back to SimPro
- β Added pipeline stage mapping configuration page
v1.0.0
15 Nov 2025
Initial Release
- β Bi-directional sync between SimPro and GoHighLevel
- β Customer, Site, Job, Quote, Lead, Invoice sync
- β Custom object support
- β Field mapping configuration
- β Event toggle controls
SimProSync by My Local Trades