A whitelabeled WordPress plugin that captures website form submissions - every field, UTM and trackable - and reliably forwards them server-side to a CRM (Freshsales first) via a configurable per-form mapping. See DESIGN.md for the full design rationale.
- Copy this folder into
wp-content/plugins/crm-connect. - Optional but recommended:
composer dump-autoload -o(a PSR-4 fallback autoloader is built in, so this is not required). - Activate CRM Connect in WP Admin → Plugins. Activation creates the queue table.
- Go to the CRM Connect menu:
- Settings - enter your Freshsales domain (
yourco.myfreshworks.com) + API key, then Test connection. Set branding, alert email/Slack, retention, auto-pause. - Mappings - add a mapping: pick a form, add a CRM object (Contact/Deal), map fields, set the dedupe key and a catch-all field. Save.
- Submissions - live log of every submission with status, full payload, CRM request/response, and retry.
- Settings - enter your Freshsales domain (
Requires PHP 8.0+, Elementor Pro (for the Elementor form source).
Form submit ─▶ FormSource (Elementor) ─▶ CaptureService ─▶ QueueStore (DB, persist-first)
│
Attribution cookie (first+last touch) ──┘
▼
QueueWorker (retries, backoff, dead-letter, 429-aware, auto-pause)
▼
CrmProvider (Freshsales) ◀─ FieldMapper (per-form profile)
- CRM is pluggable via the
crm_connect_providerfilter (CrmProviderinterface). Freshsales is provider #1. - Form platform is pluggable via the
crm_connect_form_sourcesfilter (FormSourceinterface). Elementor is source #1. - The queue, mapping, capture, log, and alerting layers are agnostic to both.
| Hook | Purpose |
|---|---|
crm_connect_provider |
Return a CrmProvider to swap CRM |
crm_connect_form_sources |
Add/replace FormSource implementations |
crm_connect_trackables |
Add custom trackable source keys |
crm_connect_attribution_enabled |
Gate the attribution cookie behind a consent manager |
crm_connect_attribution_lifetime_days |
Cookie lifetime (default 90) |
crm_connect_dead_letter / crm_connect_worker_paused |
Hook delivery failures |
- Persist-before-send: the full payload is written to the DB inside the form hook, before any CRM call - a CRM outage can never lose a submission.
- Retries with exponential backoff → dead-letter after 6 attempts.
- 429-aware: honors Freshsales'
Retry-After; schema reads are cached (rate-limit budget is 1000/hr). - Crash recovery: items stuck in
sendingfor >5 min are re-claimed. - Auto-pause after N consecutive failures + email/Slack alert; manual resume.
- Retention purge of old sent rows (configurable, default 180 days).
The plugin self-updates from GitHub Releases via Plugin Update Checker (bundled in lib/). Installed sites see the native WordPress "Update available" notice and can one-click update (or enable WordPress's per-plugin auto-update toggle for hands-off updates).
To cut a release:
bin/release.sh 0.3.0
This bumps the version, commits, tags v0.3.0, and pushes. The Release GitHub Action then builds a clean crm-connect.zip and publishes it to the release automatically.
GPL-2.0-or-later. See LICENSE.
- Reconciliation cron against Elementor's
wp_e_submissionstables (defense-in-depth backfill if the hook itself ever fatals). Deferred pending verification of Elementor's submission-table schema. - Deal orchestration: a Deal needs
name,amount,sales_account_id. The lookup-or-create-Sales-Account-by-company step is not yet wired - currently Deal fields are mapped directly like any object. create_fieldform id: Freshsales' custom-field endpoint path (settings/<entity>/forms/<form_id>/fields) uses a placeholder form id0; verify against a live account.- Choice-mapping UI for CRM dropdown fields (value → choice) is stubbed; mapping currently passes raw values (unmatched values fall to the catch-all).