CloseUp-CRM Lead Bridge — Documentation
Everything you need to install, configure, secure, and troubleshoot the plugin.
Overview
CloseUp-CRM Lead Bridge is a WordPress plugin that captures every form submission on your site and streams it into CloseUp CRM in real time — without changing anything about your existing forms. It is built for marketers, agencies, and sales teams who already collect leads through popular WordPress form plugins and want those leads in their CRM instantly, reliably, and with full marketing attribution.
Architecturally, the plugin hooks into the completion event of each supported form engine, reads the submitted fields, auto-detects the contact's name, email, and phone, enriches the record with UTM parameters and browser/device context, optionally stores it in a local table, and delivers a signed JSON payload to your configured webhook endpoint over HTTPS — all on a non-blocking path so the visitor never waits.
Requirements
- WordPress 5.6 or newer.
- PHP 7.2 or newer.
- An HTTPS webhook endpoint that can accept a POST request with a JSON body.
- A CloseUp CRM account to receive and work the incoming leads.
Installation
- In your WordPress admin, go to Plugins → Add New → Upload Plugin and choose the CloseUp-CRM Lead Bridge ZIP file.
- Click Install Now, then Activate.
wp_cll_leads database table — there are no manual database steps. It also self-checks for missing files and an unsupported PHP version, and shows a clear admin notice instead of failing silently.Quick start (5 min)
- Activate the plugin.
- Open CloseUp-CRM → Settings in your WordPress admin.
- Paste your Webhook URL and API Key (and, if you use signature verification, your Webhook Secret).
- Submit any form on your site.
- See the lead appear under CloseUp-CRM → Leads — and delivered to your CRM in real time.
Settings reference
Every field on the CloseUp-CRM → Settings screen:
| Setting | What it does |
|---|---|
| Webhook URL | The HTTPS endpoint that captured leads are POSTed to. Required for delivery. |
| Webhook Secret | Signs the request body with HMAC-SHA256. The signature is sent as X-CLL-Signature: sha256=<hmac> (alongside Authorization: Bearer <secret>). The secret itself is never sent raw in the body — it is used only to prove the payload's authenticity and integrity. |
| API Key | A simple shared key sent raw under a header so your endpoint can authenticate the caller. Unlike the secret, this value is transmitted as-is. |
| API Key Header | The header name the API key is sent under. Defaults to X-API-Key. |
| Field mapping | Optional per-form name / phone / email field IDs. Leave blank to use smart auto-detection. |
| Form engines | Enable or disable capture per provider. Each shows a live detected / not detected status for the plugins installed on your site. |
| Store leads | Toggle whether captured leads are saved to the local wp_cll_leads table. |
| Collect IP | Toggle whether the visitor IP address is recorded. Off by default for GDPR-friendliness. |
Secret vs. API key, in one line: the secret proves the body wasn't tampered with (it is never sent raw); the API key is a raw shared password that identifies the caller.
Supported form plugins
Lead Bridge taps each engine through its native completion hook, so your forms behave exactly as before.
| Form plugin | Hook tapped | Notes |
|---|---|---|
| Contact Form 7 | wpcf7_mail_sent | Reads the submitted posted data after CF7 sends its own mail. Native emails, redirects and thank-you messages are untouched. |
| Elementor / Elementor Pro | elementor_pro/forms/new_record | Taps the Pro Forms record on submit. Works with multi-step and popup forms. |
| ACF Forms | acf/save_post | Captures Advanced Custom Fields front-end form submissions on save. |
| Formidable Forms | frm_after_create_entry | Fires once the entry is stored, so the lead is captured with its final field values. |
| Fluent Forms | fluentform/submission_inserted | Reads the inserted submission; conversational and multi-column layouts supported. |
| Frontend Admin by DynamiApps | fea/form/submit | Captures front-end post/profile submissions created through DynamiApps Frontend Admin forms. |
How lead capture works
When a visitor submits a form, Lead Bridge listens on the completion hook of the relevant form engine after that engine has done its own work — sent its emails, stored its entry, and run its redirect or thank-you logic. Capture therefore never blocks or delays the submission, and native notifications and redirects are preserved untouched.
Auto-detection. The plugin inspects the submitted fields and matches them against a library of common patterns to identify the contact's name, email, and phone — even when the fields are named your-name, fname, email_address, or tel. Email is matched by value shape as a fallback, and phone by digit patterns. If you set an explicit field mapping for a form, that mapping takes priority over auto-detection.
Enrichment & delivery. The detected contact is combined with marketing attribution (from a first-party cookie) and browser/device context, optionally written to the local store, and delivered to your webhook as a signed JSON POST — all on a background, non-blocking path.
Webhook payload reference
Each captured lead is delivered as a single POST with the following JSON body. Fields are stable and safe to map directly in your CRM.
{
"event": "lead.captured",
"lead": {
"name": "Dana Cohen",
"email": "dana.cohen@example.com",
"phone": "+972-52-555-0142"
},
"form": {
"provider": "contact-form-7",
"form_id": "412",
"name": "Homepage — Request a demo",
"fields": {
"your-name": "Dana Cohen",
"email_address": "dana.cohen@example.com",
"tel": "+972-52-555-0142",
"company": "Northwind Ltd.",
"message": "Interested in the Pro plan."
}
},
"attribution": {
"utm_source": "google",
"utm_medium": "cpc",
"utm_campaign": "spring_demo",
"utm_term": "wordpress crm",
"utm_content": "hero_cta",
"gclid": "Cj0KCQiA1234EXAMPLE",
"fbclid": null
},
"enrichment": {
"browser": "Chrome 126",
"os": "macOS 14",
"device": "desktop",
"language": "en-US",
"referrer": "https://www.google.com/",
"page_url": "https://example.com/?utm_source=google&utm_medium=cpc"
},
"meta": {
"lead_id": "9f8b7c6d-5e4a-4b3c-2d1e-0f9a8b7c6d5e",
"source_url": "https://example.com/contact/",
"site": "https://example.com",
"plugin_version": "1.1.0",
"is_test": false,
"timestamp": "2026-07-22T09:14:07Z"
}
}And the exact HTTP request, including all headers:
POST /webhooks/leads HTTP/1.1
Host: crm.example.com
Content-Type: application/json
User-Agent: CloseUp-CRM-Lead-Bridge/1.1.0
X-CLL-Signature: sha256=9b2e1f0a7c... (HMAC-SHA256 of the raw body)
X-API-Key: your-shared-api-key
Authorization: Bearer your-webhook-secret
{ ...the JSON body shown above... }Security
Lead Bridge signs every request body with HMAC-SHA256 using your webhook secret and sends the result as X-CLL-Signature: sha256=<hmac>. On your endpoint, recompute the HMAC over the raw request body with the same shared secret and compare it to the header in constant time. If they match, the payload is authentic and untampered.
PHP — verify the signature:
<?php
// Read the RAW body exactly as received — do not decode + re-encode.
$rawBody = file_get_contents('php://input');
$secret = getenv('CLL_WEBHOOK_SECRET');
$header = $_SERVER['HTTP_X_CLL_SIGNATURE'] ?? ''; // "sha256=<hmac>"
$expected = 'sha256=' . hash_hmac('sha256', $rawBody, $secret);
if (!hash_equals($expected, $header)) {
http_response_code(401);
exit('Invalid signature');
}
// Optional: also require the raw API key.
$apiKey = $_SERVER['HTTP_X_API_KEY'] ?? '';
if (!hash_equals(getenv('CLL_API_KEY'), $apiKey)) {
http_response_code(401);
exit('Invalid API key');
}
$payload = json_decode($rawBody, true);
// ... handle $payload['lead'] ...Node.js (Express) — verify the signature:
import crypto from 'node:crypto';
import express from 'express';
const app = express();
// Capture the RAW body — signature must be checked against the exact bytes.
app.post(
'/webhooks/leads',
express.raw({ type: 'application/json' }),
(req, res) => {
const raw = req.body; // Buffer
const expected =
'sha256=' +
crypto
.createHmac('sha256', process.env.CLL_WEBHOOK_SECRET)
.update(raw)
.digest('hex');
const got = req.get('X-CLL-Signature') || '';
const ok =
expected.length === got.length &&
crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(got));
if (!ok) return res.status(401).send('Invalid signature');
if (req.get('X-API-Key') !== process.env.CLL_API_KEY) {
return res.status(401).send('Invalid API key');
}
const payload = JSON.parse(raw.toString('utf8'));
// ... handle payload.lead ...
res.sendStatus(200);
},
);Python (Flask) — verify the signature:
import hmac, hashlib, os
from flask import Flask, request, abort
app = Flask(__name__)
@app.post("/webhooks/leads")
def leads():
raw = request.get_data() # RAW bytes — do not use request.json here
secret = os.environ["CLL_WEBHOOK_SECRET"].encode()
expected = "sha256=" + hmac.new(secret, raw, hashlib.sha256).hexdigest()
got = request.headers.get("X-CLL-Signature", "")
if not hmac.compare_digest(expected, got):
abort(401, "Invalid signature")
if not hmac.compare_digest(
os.environ["CLL_API_KEY"], request.headers.get("X-API-Key", "")
):
abort(401, "Invalid API key")
payload = request.get_json()
# ... handle payload["lead"] ...
return "", 200X-API-Key) and compare it to your stored value before trusting the request. Use the signature to guarantee integrity and the API key to guarantee identity.Testing
The built-in Test Tool lets you prove capture and delivery without touching your live forms:
- Custom test form. Submit a built-in sample form and watch the resulting payload be captured and delivered.
- Provider self-test. Fire a synthetic submission through any enabled engine to confirm the hook is tapped and the webhook is reachable.
- Generate real demo forms. Create genuine native forms — with randomized field names — inside each installed form plugin, all collected on a single demo page, so you can verify auto-detection against realistic, unpredictable field IDs.
Every test submission is flagged with is_test: true in the payload and in the local store so it is easy to filter out of real reporting.
Leads & exports
When local storage is enabled, every captured lead is written to the branded Leads list in your WordPress admin. Open any row for a single-lead detail view showing the detected contact, the raw form fields, the full attribution, and the enrichment context.
Export the whole store at any time to CSV, XML, or JSON. Leads created by the test tools carry the is_test flag so you can include or exclude them from exports and reporting.
Marketing attribution
On first visit, Lead Bridge captures the marketing context — utm_source, utm_medium, utm_campaign, utm_term, utm_content, plus gclid and fbclid — from the URL and stores it in a first-party cookie. When the visitor later submits a form, that stored attribution is attached to the lead and passed through in the payload's attribution object, so you keep the original source even across multiple page views.
Troubleshooting
Leads not arriving
- Is the Webhook URL set and correct?
- Is the endpoint served over HTTPS and reachable from your server?
- Is the form engine toggle enabled for the plugin you are testing, and does it show as detected?
- Is a firewall or security plugin blocking outbound requests from WordPress?
Cannot activate
- Re-upload the complete ZIP — a partial upload can leave files missing.
- Confirm your server runs PHP 7.2+; the plugin shows a clear notice when it does not.
Signature mismatch
- Verify against the raw request body — do not re-encode or pretty-print the JSON before hashing, as that changes the bytes and breaks the HMAC.
- Confirm both sides use the same webhook secret.
FAQ
Expanded answers to the most common questions.
Will it change or break my existing forms?
Do I have to rename or reconfigure my form fields?
Which form plugins are supported?
How are the webhooks secured?
Is it GDPR-friendly?
Do I need to create a database table?
Changelog
Release history for the plugin. Configurable from the admin panel.
- Added Frontend Admin by DynamiApps as a supported form engine.
- New built-in test tool: generate real demo forms with randomized field names inside each installed form plugin.
- Smart field auto-detection now recognizes more common name, email, and phone field patterns.
- Webhook delivery now sends both the HMAC-SHA256 body signature (X-CLL-Signature) and a configurable API-key header.
- Local lead store gains CSV, XML, and JSON exports plus a single-lead detail view.