- PHP 78.5%
- Vue 12.1%
- JavaScript 9.1%
- Makefile 0.2%
| docs | ||
| exapp | ||
| nextcloud-app | ||
| .gitignore | ||
| AGENTS.md | ||
| LICENSE | ||
| README.md | ||
| TODO.md | ||
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 30–35 (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
- Go to Admin Settings → GHL / FG Funnels Integration
- Enter your GHL API Key (PIT token, starts with
pit-) - Enter your Location ID (sub-account ID)
- Enter a Webhook Secret (for real-time sync signature verification)
- Click Test Connection — should show contact count on success
- Enable the sync types you need (Contacts, Calendar, etc.)
- 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:
- Create a Workflow in FG Funnels
- Add a Webhook action pointing to the URL shown in settings
- 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
typefield (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 viaocc config:system:set) - PHP errors:
/var/log/nginx/nc.jevalide-ca-error.log - NC log:
/var/log/nextcloud/nextcloud.log - YunoHost warnings:
integrity.check.disabledis normal;config_is_read_onlyis 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