Scheduling and booking tool used for client discovery calls and demos; embedded on Webflow landing pages
Cal.com
Scheduling and booking platform used for client discovery calls, demos, and intake flows. Embedded on Webflow landing pages as an inline or pop-up widget; integrated with GTM and first-party user-id tracking for conversion attribution.
Current Useâ
- Booking pop-ups on Webflow landing pages via the Cal.com embed script
- Multiple event types managed by a single Cal.com Multi-Widget script (shared singleton modal, multiple buttons per page)
- User-id (
_c_pca/_c_sct) passed into the embed via prefill parameters for cross-session attribution - Booking completion tracked via Cal.com webhooks or PostMessage to parent page
dataLayer
How It Worksâ
Cal.com generates an embed snippet per calendar/event type. On Webflow, all booking CTA buttons are wired to a single modal instance managed by the Multi-Widget script, which reads the event type from each button's data-cal-link attribute and opens the correct booking flow.
Embed Workflowâ
- Include the Cal.com embed script once in page
<head>custom code - Add
data-cal-link="pacing/discovery"(or relevant event slug) to each CTA button - Multi-Widget script initialises one Cal modal and delegates all button clicks to it
- On page load, user-id is read from
localStorage/ cookie and injected into the embed as a prefill metadata field - On booking completion, Cal.com fires a PostMessage to the parent window; the page script pushes a
cal_booking_completeevent todataLayerfor GTM
Tracking Architectureâ
| Signal | Method | Where it lands |
|---|---|---|
| User-id on booking | Prefill metadata[user_id] in embed config | Cal.com booking record |
| Booking completion (client-side) | PostMessage â dataLayer.push | GTM â GA4 / Google Ads |
| Booking completion (server-side) | Cal.com webhook â sGTM | GA4 / BigQuery |
See Third-Party Embeds and Tracking for the full implementation guide.
Account Accessâ
- Access: Cal.com Dashboard
- Account type: Internal (Pacing-managed) and client access where applicable
- Credentials: Stored in password manager
- API Keys: Stored in root
.envfile asCAL_API_KEYif required by automation
Configurationâ
Embed Snippet (page <head>)â
<script>
(function (C, A, L) {
let p = function (a, ar) { a.q.push(ar); };
let d = C.document;
C.Cal = C.Cal || function () { let cal = C.Cal; let ar = arguments; if (!cal.loaded) { cal.ns = {}; cal.q = cal.q || []; d.head.appendChild(d.createElement("script")).src = A; cal.loaded = true; } if (ar[0] === L) { const api = function () { p(api, arguments); }; const namespace = ar[1]; api.q = api.q || []; typeof namespace === "string" ? (cal.ns[namespace] = api) && p(api, ar) : p(cal, ar); return; } p(cal, ar); };
})(window, "https://app.cal.com/embed/embed.js", "init");
Cal("init", { origin: "https://cal.com" });
</script>
Button markupâ
<button data-cal-link="pacing/discovery" data-cal-config='{"layout":"month_view"}'>
Book a call
</button>
Replace pacing/discovery with the relevant Cal.com username/event slug.
User-id injection (before </body>)â
// Inject user-id into Cal.com embed prefill
(function () {
var uid = localStorage.getItem('_c_pca') || getCookie('_c_pca') || '';
if (uid && window.Cal) {
Cal('ui', {
metadata: { user_id: uid }
});
}
})();
PostMessage listener for booking completionâ
window.addEventListener('message', function (e) {
if (e.data && e.data.type === '__NEXT_DATA_ROUTER_STATE_CHANGE__') { return; }
if (e.data && e.data.type === 'booking_successful') {
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({ event: 'cal_booking_complete', user_id: e.data.booking?.metadata?.user_id || '' });
}
});
Check the current Cal.com embed docs for the exact PostMessage event name - it can change between versions.
Features in Useâ
Inline and Pop-up Embedsâ
Cal.com supports inline (renders in page), pop-up (modal on button click), and floating button modes. Pacing uses pop-up mode on landing pages so the booking flow overlays the page without navigation.
Multi-Widget Scriptâ
A reusable script that manages all Cal.com pop-ups on a page with a single shared modal instance. Prevents multiple Cal instances conflicting on pages with several CTA buttons for different event types.
See Webflow â Snippets & Reusable Patterns for the full pattern.
Event Typesâ
Cal.com event types define meeting length, availability, and intake questions. Common event types used:
| Slug | Purpose |
|---|---|
discovery | Initial discovery call |
demo | Product/service demo |
Update this table with the actual slugs for each client/account.
Integrationsâ
With Webflowâ
Cal.com is embedded directly into Webflow pages via custom code in Page Settings. The Multi-Widget script handles delegation across multiple buttons.
See Webflow for embedding patterns.
With GTM and GA4â
Cal.com runs inside an iframe on Cal.com's domain, so GTM on the parent page does not fire inside it. Attribution relies on:
- User-id prefill passed into the embed before load
- PostMessage listener on the parent page pushing a
cal_booking_completedataLayerevent - Cal.com webhook (optional) to server-side GTM for a guaranteed server-side conversion signal
See Third-Party Embeds and Tracking and GTM for implementation details.
With sGTM / BigQueryâ
If Cal.com webhooks are configured, the booking payload (including metadata fields like user_id) can be routed to server-side GTM and logged to BigQuery. This gives a reliable server-side signal independent of browser-side PostMessage.
Costs and Billingâ
- Monthly Cost: Free tier (confirm if upgraded)
- Billing Cycle: N/A (free) or annual if on paid plan
- Next Renewal: N/A
- Payment Method: N/A
Update with actual plan details if the account is on a paid tier.
Documentationâ
Official Resourcesâ
Internal Resourcesâ
- Third-Party Embeds and Tracking - User-id and GTM integration guide
- Client Landing Page Setup - Full implementation phases including Cal.com
- Webflow Snippets & Reusable Patterns - Cal.com Multi-Widget pattern
Troubleshootingâ
Common Issuesâ
Issue: Multiple Cal instances conflict when several CTA buttons exist on a page
Solution: Use the Multi-Widget script so all buttons share one modal instance. Never initialise Cal("init") more than once per page.
Issue: PostMessage listener fires but no dataLayer event reaches GTM
Solution: Confirm the PostMessage event type name against the current Cal.com embed version. Log e.data in console to inspect the actual payload.
Issue: User-id not appearing in booking metadata
Solution: Ensure the user-id injection script runs after the Cal.com embed script loads. Check localStorage / cookie is populated before the inject runs.
Issue: Booking completion not registering as a conversion in Google Ads
Solution: Verify the sGTM webhook route is receiving the Cal.com webhook POST and that the user_id field is mapped correctly to the conversion event.
TODOâ
- Confirm account details and add real accountId from TwentyCRM
- Document the actual event type slugs in use per client
- Confirm whether account is on free or paid plan and update billing section
- Add
CAL_API_KEYto.envdocumentation if API automation is implemented - Finalise and document the PostMessage event name for the current Cal.com embed version
Related Toolsâ
- Webflow - Landing pages where Cal.com is embedded
- GTM - Tag management for conversion tracking
- n8n - Potential automation for webhook routing
- Third-Party Embeds and Tracking - Implementation guide
Note Context
Importance Rating: Medium - Used on active client landing pages for booking flows; not mission-critical infrastructure but directly impacts lead capture.
Focus Areas for Note:
- Cal.com is a booking/scheduling tool, not a full CRM - note is placed in CRM category because it handles client intake and booking data
- Key differentiator: the tracking integration (user-id prefill + PostMessage/webhook) is non-trivial and is the main operational concern
- The Multi-Widget script is the primary Pacing-built pattern on top of Cal.com
Additional Context:
- accountId is a placeholder - needs updating once synced to TwentyCRM
- Free plan details to be confirmed
Last Updated: March 2026
Owner: Ben Power
Status: Active