Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.apifycloud.io/llms.txt

Use this file to discover all available pages before exploring further.

Every Click to Call widget has a Custom code section where you can paste HTML and JavaScript snippets. The code runs inside a secure sandbox, so it can react to widget events and call its own APIs without putting your visitors or your account at risk.

Where to configure it

In the console under Call Studio → Settings → Custom code, two text areas:
  • Head HTML — injected at the top of the sandbox document. Use for <script> tags that set up SDKs, <link> tags for CSS, meta tags.
  • Body end HTML — injected at the bottom. Use for scripts that need the DOM ready or that fire on visibility.
Both areas accept arbitrary HTML. <script> tags execute. Inline scripts, external scripts (via src), and <link> stylesheets all work.

The sandbox model

The code you paste runs inside an isolated iframe with a strict sandbox. What the sandbox can do:
  • Run any JavaScript
  • Load external scripts and stylesheets
  • Call third-party APIs (analytics, pixels, SDKs) over the network
  • Subscribe to widget events
  • Emit custom events back to the widget
What the sandbox cannot do:
  • Read cookies or localStorage on the integrator’s domain
  • Read cookies or localStorage of apifycloud.io
  • Access the widget’s DOM or React state
  • Call the integrator’s APIs with the user’s credentials
  • Navigate the top-level window
  • Read or modify the embedding page in any way

The window.c2c API

Inside the sandbox, window.c2c is pre-populated with a small event bridge to the widget. Same surface as if it were an uninstrumented page — you just read events and emit events.

Subscribe to events

window.c2c.on('call_started', (event) => {
  console.log('Call began', event.context);
});

window.c2c.on('call_ended', (event) => {
  // Send to your analytics
  ga4Track('call_ended', {
    duration: event.duration,
    reason: event.reason,
    order_id: event.context.orderId,
  });
});
All events from the Events catalogue are available. The callback receives the full data object including context.

Unsubscribe

on returns an unsubscribe function. Call it to stop receiving events:
const off = window.c2c.on('call_ended', handler);
// ...later
off();
Or use window.c2c.off(name, handler) explicitly.

Emit custom events

window.c2c.emit('custom_action', { kind: 'chat_opened' });
Your custom events surface in the parent page and in Integrations under the prefix custom:custom_action.

Read URL context

console.log(window.c2c.context.orderId);
console.log(window.c2c.appId);
The context object is frozen — you can read but not modify it.

Await ready

The shim dispatches a c2c:ready event on its own window as soon as the bridge is ready:
window.addEventListener('c2c:ready', () => {
  // window.c2c is fully initialised
});
In practice, window.c2c is defined synchronously before your scripts execute, so you rarely need to wait.

Common examples

GA4 — track calls as events

<script async src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXX"></script>
<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){ dataLayer.push(arguments); }
  gtag('js', new Date());
  gtag('config', 'G-XXXXXXX');

  window.c2c.on('call_started', () => gtag('event', 'call_started'));
  window.c2c.on('call_ended', (e) => {
    gtag('event', 'call_ended', {
      duration: e.duration,
      reason: e.reason,
    });
  });
  window.c2c.on('survey_submitted', (e) => {
    gtag('event', 'survey_submitted', { rating: e.rating });
  });
</script>

Meta Pixel — fire Lead on call start

<script>
  !function(f,b,e,v,n,t,s){/* Meta Pixel snippet */}(
    window, document,'script','https://connect.facebook.net/en_US/fbevents.js');
  fbq('init', 'YOUR_PIXEL_ID');
  fbq('track', 'PageView');

  window.c2c.on('call_started', () => fbq('track', 'Lead'));
</script>

Google Tag Manager — push events to the dataLayer

<script>
  window.dataLayer = window.dataLayer || [];

  window.c2c.on('call_started', (e) => {
    window.dataLayer.push({ event: 'c2c_call_started', context: e.context });
  });
  window.c2c.on('call_ended', (e) => {
    window.dataLayer.push({
      event: 'c2c_call_ended',
      duration: e.duration,
      reason: e.reason,
      context: e.context,
    });
  });
</script>

Custom events — emit your own signals

Use window.c2c.emit to surface custom signals from the sandbox. They reach the parent page as postMessage and integrations as custom:<name>.
<script>
  // Let your own logic fire a named event when, for example, a user
  // interacts with a chat widget you loaded inside the sandbox.
  function onChatOpened() {
    window.c2c.emit('chat_opened', { source: 'widget' });
  }
</script>

Conditional tracking based on URL context

The context object is available on every event and on window.c2c.context. Use it to only fire a tag for specific visitor cohorts.
<script>
  window.c2c.on('call_ended', (e) => {
    if (e.context.customerTier === 'gold' && e.duration > 30) {
      // Your own tracking call here.
    }
  });
</script>

Limitations to keep in mind

Because the sandbox has an opaque origin, it has its own cookie jar. Any SDK that depends on first-party cookies on your domain will create them inside the sandbox instead, not on the embedding page.
You can’t read URL parameters from the embedding page, inspect its DOM, or modify it. Pass anything you need via URL context when loading the widget.
Communication with the widget is event-based and one-way per direction (sandbox → widget and widget → sandbox). There’s no request/response RPC pattern built in.
WebRTC audio streams live in the widget, not in the sandbox. Custom code cannot tap into mic input or call audio.

Debugging custom code

The sandbox is a normal iframe — open your browser devtools and select it in the iframe dropdown to inspect its console, network, and sources. Errors in your custom code appear there, not in the main page console. If a script fails silently:
  1. Check the Network tab for blocked requests (CSP on your side, ad blockers, mixed-content warnings).
  2. Check the Console for thrown exceptions inside handlers.
  3. Verify the iframe’s sandbox attributes are what you expect — by design they should be allow-scripts only.

What’s next

Events

The full catalogue of events you can subscribe to.

Security

Why the sandbox is shaped this way and what it protects against.