Docs
Everything from "drop the snippet" to reading your first leads — plus signed webhooks for pushing leads into your own tools.
Installation
1. Install the snippet
Add this single line of JavaScript to your site's <head> tag.
<script src="https://clickmemaybe.com/cmm/YOUR_SITE_KEY.js" defer></script>
Replace YOUR_SITE_KEY with the site key from your Websites page — or just copy the ready-made snippet from the site's Manage page.
2. CMS-specific guides
There is no plugin to install — every platform below has a place to paste one line of HTML into <head>. Copy the snippet from your site's Manage page first.
WordPress
- Open Appearance → Theme File Editor (or use a free "Insert Headers and Footers" plugin — WPCode, or the one bundled with your theme).
- Paste the snippet into the Header /
<head>scripts box and save. - Purge any caching plugin (WP Rocket, LiteSpeed, W3TC) and reload your site.
Shopify
- In the admin go to Online Store → Themes → ⋯ → Edit code.
- Open
layout/theme.liquidand paste the snippet right before</head>. - Save. The widget loads on every storefront page; use page rules to limit it (e.g.
/products/*).
Webflow
- Open Site settings → Custom code.
- Paste the snippet into Head code and save.
- Publish the site — custom code is only live after publishing.
Squarespace
- Go to Settings → Advanced → Code Injection (Business plan or higher).
- Paste the snippet into the Header field and save.
- Reload a public page; code injection is not shown in the editor preview.
Wix
- Open Settings → Custom code (requires a premium plan with a connected domain).
- Click Add custom code, paste the snippet, choose All pages and place it in Head.
- Apply and publish.
Custom HTML / SPA (React, Vue, Svelte, Next.js)
- Add the snippet to the
<head>of your HTML template orindex.html; for Next.js usenext/scriptwithstrategy="afterInteractive". - The loader is
deferand self-contained — no npm package, no build step. - Single-page apps: the widget reads the URL on each render, so page rules work after client-side navigation too.
3. Verify the script
Open your site, then come back to the dashboard. Your site card should show "Live" within 30 seconds.
Widgets
Create your first widget
Pick a category, drop in your headline and CTA, and pick which site to show it on. You can refine targeting and design later.
Widget types
-
Popup —
popuplive -
Sticky Bar —
sticky_barlive -
Floating CTA —
floating_ctalive -
Inline Form —
formlive -
Exit Intent —
exit_intentlive -
Countdown —
countdownlive -
Social Proof —
social_prooflive -
Announcement —
announcementlive -
Callback —
callbacklive -
Slide-in —
slide_inlive -
Welcome Mat —
fullscreenlive -
Survey / NPS —
surveylive -
Multi-step / Quiz —
multi_steplive -
Age Gate —
age_gatelive -
Video Popup —
video_popuplive -
Cookie Consent —
cookie_consentlive -
Sticky Add-to-cart —
sticky_atclive -
Back-in-stock —
back_in_stocklive -
Live Visitors —
live_visitorslive -
Spin-to-win —
spin_to_winlive -
Reviews —
reviewslive -
FAQ —
faqlive -
Morph —
morphsoon
Design and brand
Each widget takes an accent color (button and highlights; the darker hover shade is derived automatically) and, on the callback widget, a screen position. Widgets inherit your page's font so they never look foreign. Brand fonts and a custom CSS escape hatch are on the roadmap; paid plans remove the "Powered by ClickMeMaybe" badge.
Targeting
Page rules
Show a widget only on selected paths (exact path such as /pricing, or a prefix such as /blog/*) and exclude paths where it must never appear. Exclusions win. Rules match the page path, not the query string.
Device
Target all devices, desktop only, or mobile only (viewports narrower than 768px). Geo, language, returning visitors, referrer and UTM targeting are on the roadmap.
Triggers
Each widget type has a built-in trigger: on-load popups appear shortly after the page is ready, exit-intent popups fire when the cursor leaves the viewport (timer + scroll-up on touch devices), bars and floating buttons render immediately. Scroll-depth, time-on-page and custom-event triggers are on the roadmap.
Analytics & Leads
Tracked events
We track impressions (widget rendered), clicks (interaction), and submissions (form completion). Custom events coming soon.
Working with leads
Leads land in your inbox in real time. Mark them new, reviewed, or contacted, and export as CSV anytime.
API & Integrations
REST API coming soon
List and create widgets, query analytics, and pull leads — all over a clean REST API.
Webhooks live
Get every new lead pushed to your own endpoint the moment it arrives. Open your website → Install → Security, paste an https:// URL and save. A signing secret is generated and shown once — store it.
Request
We send a POST with a JSON body and these headers:
Content-Type: application/json X-CMM-Event: lead.created X-CMM-Signature: sha256=<hex HMAC-SHA256 of the raw body, keyed with your secret> User-Agent: ClickMeMaybe-Webhook/1.0
Payload — lead.created
{
"event": "lead.created",
"created_at": "2026-09-03T10:15:42+00:00",
"lead": {
"id": 1234,
"email": "anna@example.com",
"phone": null,
"name": "Anna",
"page": "https://yoursite.com/pricing",
"device": "mobile",
"type": "optin",
"message": null,
"score": null,
"answers": null,
"product": null,
"prize": null,
"code": null
},
"widget": { "id": 42, "name": "Exit popup — yoursite.com", "category": "exit_intent" },
"website": { "id": 7, "domain": "yoursite.com" }
}
lead.type is one of callback, message, optin, contact, form, survey, quiz, back_in_stock, spin (falls back to the widget category). lead.message is the message or comment, lead.score the 0–10 NPS score (survey), lead.answers a list of {q, a} pairs from a quiz. lead.product is the product name from a back-in-stock form. lead.prize and lead.code are the spin-to-win segment and its coupon code. Fields that don't apply are null.
Delivery & retries
- Respond with any 2xx within 5 seconds to acknowledge. Do the heavy work asynchronously.
- Non-2xx responses, timeouts and connection errors are retried 3 times in total (after ~10 s, 60 s and 5 min). After that the delivery is marked failed.
- Deliveries are sent from a queue — the visitor's widget never waits on your server.
- Treat
lead.idas the idempotency key: a retry may re-send the same lead.
Verifying the signature
Always compute the HMAC over the raw request body (before JSON parsing) and compare in constant time.
$secret = getenv('CMM_WEBHOOK_SECRET');
$body = file_get_contents('php://input');
$expected = 'sha256=' . hash_hmac('sha256', $body, $secret);
$given = $_SERVER['HTTP_X_CMM_SIGNATURE'] ?? '';
if (! hash_equals($expected, $given)) {
http_response_code(401);
exit('bad signature');
}
$event = json_decode($body, true);
// $event['lead']['email'] ...
http_response_code(204);
import crypto from 'node:crypto';
import express from 'express';
const app = express();
const SECRET = process.env.CMM_WEBHOOK_SECRET;
// Keep the raw body — the signature is computed over the exact bytes we sent.
app.post('/cmm-webhook', express.raw({ type: 'application/json' }), (req, res) => {
const expected = 'sha256=' + crypto.createHmac('sha256', SECRET).update(req.body).digest('hex');
const given = req.get('X-CMM-Signature') || '';
if (expected.length !== given.length || !crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(given))) {
return res.status(401).send('bad signature');
}
const event = JSON.parse(req.body.toString('utf8'));
// event.lead.email ...
res.sendStatus(204);
});
Rotating the secret (Regenerate secret in Security) invalidates the old one immediately — update your endpoint first, then rotate.
Zapier, Make, n8n via webhook
No native app needed. In Zapier create a Zap with the trigger Webhooks by Zapier → Catch Hook, copy the generated URL and paste it as your webhook URL in Security. Send a test lead from your widget, then map lead.email, lead.name etc. into any of 5,000+ apps. The same works with Make (Custom webhook) and n8n (Webhook node).