GHL / FG Funnels and Nextcloud Integration
  • PHP 78.5%
  • Vue 12.1%
  • JavaScript 9.1%
  • Makefile 0.2%
Find a file
François Pelletier 0f46620c96 Contact Sync fix
2026-07-13 14:07:55 -04:00
docs ajout infos auteur 2026-07-12 21:47:31 -04:00
exapp version initiale 2026-07-11 23:02:47 -04:00
nextcloud-app Contact Sync fix 2026-07-13 14:07:55 -04:00
.gitignore test + impl: sync services, target selectors, admin user context, 59 tests 2026-07-12 23:57:15 -04:00
AGENTS.md feat: CLI commands (ghl:sync, ghl:status, ghl:test-connection) + 70 tests 2026-07-13 00:38:44 -04:00
LICENSE ajout infos auteur 2026-07-12 21:47:31 -04:00
README.md feat: CLI commands (ghl:sync, ghl:status, ghl:test-connection) + 70 tests 2026-07-13 00:38:44 -04:00
TODO.md feat: CLI commands (ghl:sync, ghl:status, ghl:test-connection) + 70 tests 2026-07-13 00:38:44 -04:00

GHL / FG Funnels — Nextcloud Integration

Bidirectional sync between GoHighLevel (FG Funnels) and Nextcloud.

Features

  • Contacts — GHL contacts → Nextcloud Address Book (CardDAV vCards)
  • Calendar — GHL appointments → Nextcloud Calendar (CalDAV iCal events)
  • Opportunities — GHL pipelines/stages → Nextcloud Deck boards/cards
  • Files & Media — GHL media files → Nextcloud Files (GHL_Media/)
  • Invoices — GHL invoice PDFs → Nextcloud Files (GHL_Invoices/)
  • Form Attachments — Form file uploads → Nextcloud Files (GHL_Forms/{name}/)
  • Real-time — Webhook-triggered sync from GHL/FG Funnels
  • Dashboard Widget — GHL KPIs on Nextcloud dashboard
  • Nextcloud Flow — Trigger GHL actions from Nextcloud workflows (create contact, add to pipeline)
  • Background Sync — Configurable cron job (default every 15 min)

Requirements

  • Nextcloud 3035 (tested on NC 34.0.1)
  • PHP 8.1+ (tested on PHP 8.5.8)
  • Composer
  • Node.js 20+ (for frontend build only)
  • GHL / FG Funnels account with Private Integration Token (PIT)

Install

1. Clone and build

git clone https://git.jevalide.ca/partage/ghl-nextcloud-integration
cd ghl-nextcloud-integration/nextcloud-app

2. Install PHP dependencies

composer install --no-dev --no-interaction

3. Build Vue.js frontend

npm install --legacy-peer-deps
npm run build

This produces js/ghl-integration-settings.js (IIFE format, ~110 KB).

4. Deploy to Nextcloud

# Copy app (adjust NC path for your installation)
rsync -avz --exclude 'node_modules' --exclude 'src' --exclude 'vite.config.js' \
  nextcloud-app/ /var/www/nextcloud/apps/ghl_integration/

# Fix ownership
chown -R www-data:www-data /var/www/nextcloud/apps/ghl_integration

5. Enable the app

# As root or www-data depending on your setup
sudo -u www-data php /var/www/nextcloud/   occ app:enable ghl_integration

This creates the oc_ghl_sync_mappings database table automatically.

5b. (Optional) Run tests

cd nextcloud-app
composer install  # includes dev deps
php vendor/bin/phpunit
# 70 tests, 190 assertions — all pass

6. Configure

  1. Go to Admin Settings → GHL / FG Funnels Integration
  2. Enter your GHL API Key (PIT token, starts with pit-)
  3. Enter your Location ID (sub-account ID)
  4. Enter a Webhook Secret (for real-time sync signature verification)
  5. Click Test Connection — should show contact count on success
  6. Enable the sync types you need (Contacts, Calendar, etc.)
  7. Click Save Settings

Default sync direction: GHL → Nextcloud (one-way). Bidirectional mode is available per entity type but the NC → GHL direction is not yet implemented (see TODO.md).

7. Set up webhooks (for real-time sync)

Important: FG Funnels does NOT have outgoing webhooks in Settings → Webhooks. Instead:

  1. Create a Workflow in FG Funnels
  2. Add a Webhook action pointing to the URL shown in settings
  3. The webhook URL is: https://your-nextcloud.com/index.php/apps/ghl_integration/webhook

Configure the workflow trigger for the events you want to sync (ContactCreate, AppointmentCreate, etc.).

8. CLI Commands

The app provides three occ commands:

# Check sync status (counts, modes, last sync)
sudo -u www-data php /var/www/nextcloud/occ ghl:status

# Test GHL API connection
sudo -u www-data php /var/www/nextcloud/occ ghl:test-connection

# Run sync manually
sudo -u www-data php /var/www/nextcloud/occ ghl:sync
sudo -u www-data php /var/www/nextcloud/occ ghl:sync --entity contacts --entity calendar
sudo -u www-data php /var/www/nextcloud/occ ghl:sync --no-flush

# Verbose output (detailed per-item log)
sudo -u www-data php /var/www/nextcloud/occ ghl:sync -v

9. Verify

sudo -u www-data php /var/www/nextcloud/occ app:list --enabled | grep ghl
# Expected: ghl_integration 1.0.0

Project structure

nextcloud-app/                  PHP Nextcloud app (primary)
  appinfo/
    info.xml                    App metadata, dependencies, settings registration
    routes.php                  7 routes: settings CRUD, test, sync, webhook
  lib/
    AppInfo/Application.php     NC bootstrap (registers dashboard widget only)
    Command/
      Sync.php                  occ ghl:sync — run sync from CLI
      TestConnection.php         occ ghl:test-connection — test GHL API
      Status.php                 occ ghl:status — show sync status
    Controller/
      SettingsController.php    Settings CRUD, test connection, trigger sync, dashboard data
      WebhookController.php     Webhook receiver (GHL + FG Funnels payloads)
    Service/
      GhlApiService.php         Guzzle HTTP client for GHL API
      SyncMappingService.php    CRUD for ghl_sync_mappings DB table
      ContactSyncService.php    Contact sync (CardDAV via IManager)
      CalendarSyncService.php   Calendar sync (CalDAV via CalDavBackend)
      DeckSyncService.php       Deck/opportunity sync (Board/Stack/Card creation)
      FileSyncService.php       File download & storage (IRootFolder)
      FormAttachmentSyncService.php  Form attachment extraction & storage
    BackgroundJob/
      SyncJob.php               Timed cron job (configurable interval)
    Dashboard/
      GhlWidget.php             Dashboard KPI widget
    Flow/
      GhlContactOperation.php   NC Flow → GHL create contact
      GhlPipelineOperation.php  NC Flow → GHL add to pipeline
    Migration/
      Version20250101000000.php DB schema (ghl_sync_mappings table)
    Settings/
      Admin.php                 ISettings implementation (loads Vue app)
      Section.php               IIconSection implementation (admin sidebar)
  src/
    views/GhlAdminSettings.vue  Vue 3 admin settings UI
  js/
    ghl-integration-settings.js Built IIFE bundle (~110 KB)
  img/
    ghl-icon.svg                Admin section icon
    ghl-logo-full.png           Full color logo
    ghl-logo-white.png          White logo variant
  templates/
    settings.php                PHP template wrapper for Vue mount point
  composer.json                 PHP deps (guzzle 7.x)
  package.json                  Frontend deps (Vue 3, Vite)

exapp/                          Node.js ExApp (optional Docker microservice)
  src/                          Express server, GHL client, sync engine, webhook handler
  Dockerfile                    Node 20 Alpine
  Makefile                      Build/deploy helpers

docs/
  architecture.md               Data flow diagrams, entity mappings
  setup.md                      Full install guide

GHL API Details

Component Value
Base URL https://services.leadconnectorhq.com
No /v3/ prefix Endpoints are /contacts/, /opportunities/, etc. directly
Version header Version: 2021-07-28 (required)
Auth Authorization: Bearer <PIT token>
Pagination limit + offset query params
Rate limiting 429 responses with Retry-After header

FG Funnels Webhook Differences

FG Funnels uses a different webhook mechanism than standard GHL:

Standard GHL FG Funnels
Setup Settings → Webhooks Workflow → Action → Webhook
Signature sha256=<HMAC hex> Raw secret string
Payload Has type field No type field

The WebhookController handles both formats:

  • GHL payloads: dispatched by type field (ContactCreate, etc.)
  • FG payloads: entity type inferred from payload structure (contactId → contact, calendar → appointment, etc.)

Signature verification accepts: raw secret match, sha256=<hmac>, sha1=<hmac>, or raw hex.

Server-specific notes (YunoHost / admin.jevalide.ca)

For reference, these are the specifics of the production deployment:

  • OS: Debian (YunoHost), NC 34.0.1, PHP 8.5.8
  • NC root: /var/www/nextcloud
  • SSH: francois@admin.jevalide.ca -p 2022 -i ~/.ssh/ovhdedie
  • Root access: sudo -n (NOPASSWD)
  • Config: config_is_read_only=true (set via occ config:system:set)
  • PHP errors: /var/log/nginx/nc.jevalide-ca-error.log
  • NC log: /var/log/nextcloud/nextcloud.log
  • YunoHost warnings: integrity.check.disabled is normal; config_is_read_only is intentional

Troubleshooting

Issue Fix
Settings page shows "Failed to load settings" Hard-refresh (Ctrl+Shift+R). The JS bundle uses native fetch(), not @nextcloud/axios
Connection test fails 1) Verify API key starts with pit- 2) Check Location ID 3) Ensure Version: 2021-07-28 header (built-in)
Webhook returns 403 "Invalid signature" For FG Funnels: the secret is sent raw, not as HMAC. Ensure webhook_secret matches FG workflow config
Webhook returns 500 Check /var/log/nginx/nc.jevalide-ca-error.log for PHP errors
registerFlowOperation() error Old version — ensure you have the latest code (Application.php only uses registerDashboardWidget)
ISection not found Old version — Section.php must implement IIconSection, not ISection
No sync_mappings in DB Sync services are currently stubs — see TODO.md for implementation status
Logo 404 Static assets served from /apps/ghl_integration/img/ — do NOT prepend /index.php/
"multiple forms" warning Remove <form> wrapper from Vue template — NC admin wraps in its own form
Config empty after save Ensure you clicked Save before Test. Settings are in oc_appconfig table

License

AGPL-3.0-only