Skip to main content

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​

  1. Include the Cal.com embed script once in page <head> custom code
  2. Add data-cal-link="pacing/discovery" (or relevant event slug) to each CTA button
  3. Multi-Widget script initialises one Cal modal and delegates all button clicks to it
  4. On page load, user-id is read from localStorage / cookie and injected into the embed as a prefill metadata field
  5. On booking completion, Cal.com fires a PostMessage to the parent window; the page script pushes a cal_booking_complete event to dataLayer for GTM

Tracking Architecture​

SignalMethodWhere it lands
User-id on bookingPrefill metadata[user_id] in embed configCal.com booking record
Booking completion (client-side)PostMessage → dataLayer.pushGTM → GA4 / Google Ads
Booking completion (server-side)Cal.com webhook → sGTMGA4 / 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 .env file as CAL_API_KEY if 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:

SlugPurpose
discoveryInitial discovery call
demoProduct/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:

  1. User-id prefill passed into the embed before load
  2. PostMessage listener on the parent page pushing a cal_booking_complete dataLayer event
  3. 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​

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_KEY to .env documentation if API automation is implemented
  • Finalise and document the PostMessage event name for the current Cal.com embed version
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