> For the complete documentation index, see [llms.txt](https://docs.spara.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.spara.com/developers/spara-api/javascript-api.md).

# Javascript API

How to leverage JavaScript to read and/or write data to Spara.

## Subscribing to Spara conversion events

`SparaActions` exposes subscription methods for conversion events from any Spara widget — chat, voice, and AI Product Demo. This is the recommended way to feed Spara events into Google Tag Manager, GA4, Google Ads, or LinkedIn Ads conversion tracking.

* `SparaActions.onCtaClick(handler)` — the visitor clicked a conversion CTA, e.g. **Talk to Sales** in a Product Demo. Payload: `{ label, url?, formFactor }`
* `SparaActions.onMessageSent(handler)` — the visitor sent a chat message. Payload: `{ text, formFactor }`
* `SparaActions.onMeetingBooked(handler)` — the visitor completed a calendar booking. Payload: `{ formFactor }`
* `SparaActions.onEmailCaptured(handler)` — the visitor's email address was captured and confirmed. Payload: `{ email, formFactor }`
* `SparaActions.onDemoStarted(handler)` — an AI Product Demo was opened. Payload: `{ formFactor }`
* `SparaActions.onDemoEnded(handler)` — the Product Demo closed. Payload: `{ formFactor, reason, durationSeconds }`, where `reason` is `call_ended` (the demo ran to completion) or `closed_early` (the visitor closed it)

Every payload includes a `formFactor` field identifying which widget the event happened in: `NAVIGATOR`, `SMARTBAR`, `FULLSCREEN`, `ASK_BAR`, `PRODUCT_DEMO`, or `SCHEDULING` — the same form-factor vocabulary used across Spara analytics.

{% hint style="info" %}
If a visitor closes the browser tab mid-demo, no `onDemoEnded` event fires, so started/ended counts won't reconcile exactly.
{% endhint %}

You can register handlers at any time — before or after the embed script loads — and register multiple handlers for the same event (e.g. one from your site code and one from a GTM tag). For example, pushing Product Demo conversions to the GTM data layer:

```html
<script type="text/javascript" src="https://app.spara.co/embed-<app_id>.js"></script>
<script>
  SparaActions.onMeetingBooked(function (e) {
    window.dataLayer = window.dataLayer || [];
    window.dataLayer.push({ event: 'spara_meeting_booked', spara_form_factor: e.formFactor });
  });
  SparaActions.onCtaClick(function (e) {
    if (e.formFactor === 'PRODUCT_DEMO' && e.label === 'talk_to_sales') {
      window.dataLayer.push({ event: 'spara_demo_talk_to_sales' });
    }
  });
  SparaActions.onEmailCaptured(function (e) {
    window.dataLayer.push({ event: 'spara_email_captured', spara_form_factor: e.formFactor });
  });
</script>
```

## Reading Javascript events from Spara

{% hint style="info" %}
The `window.Spara` callbacks below still work, but they support only one handler each and don't identify which widget an event came from. For new integrations, prefer the `SparaActions.on*` subscriptions described in [#subscribing-to-spara-conversion-events](#subscribing-to-spara-conversion-events "mention").
{% endhint %}

Spara's JavaScript API exposes three events to the DOM:

* `Spara.onSparaLoad()` fires when Spara's embed script finishes executing
* `Spara.onUserMessageSent()` fires when a lead sends message to Spara
* `Spara.onUserScheduled()` fires when a lead schedules a call

These can be used however you like. For example, by pushing these events to your window's data layer for ingestion by Google Analytics.

To access these Javascript events, you will need to add an additional script to your website's `<head>`:

```html
<script type="text/javascript" src="https://app.spara.co/embed-<app_id>.js"></script>
<script>
  Spara = {
    onSparaLoad: () => {
        console.log("Spara is loaded! 🎬");
    },
    onUserMessageSent: (text) => { 
        console.log("User sent a message to Spara 💬");
    },
    onUserScheduled: () => {
        console.log("User has scheduled a call");
    }
  }
</script>
```

## Writing information to Spara using Javascript

Spara's Javascript API exposes methods to write information to Spara Lead objects. For more information on how leads are represented in our database see our documentation on Lead [Webhooks](/developers/spara-api/webhooks.md#schema).

`SparaActions.setFields` accepts a dictionary of arbitrary key/value pairs assigned to the current website visitor. For example:

```javascript
SparaActions.setFields({
  acme_uuid: '1234567',  // This is an arbitrary field and value
  experiment_group: 'control'
}
```

To specifically send Spara information on page load, you will need to add an additional Javascript snippet immediately after initializing Spara's Javascript embed.

Here is a full example, which sets the current visitor's "ACME uuid" field:

```html
    <script type="text/javascript" src="https://app.spara.co/embed-<app_id>.js"></script>
    <script>
      Spara = {
        fields: {
          acme_uuid: '1234567'  // This is an arbitrary field and value
        }
      }
    </script>
```

#### Note on Query Parameters

A reminder that Spara automatically ingests all query parameters from your webpages, as long as they are mapped in the Spara platform. See [Query Parameters](/developers/installation/query-parameters.md) for more information.

## Opening Spara using Javascript

Spara provides JS functions to dynamically open Spara Navigator, Smartbar, and Product Demo.

* `SparaActions.openChat()` - opens Navigator
* `SparaActions.openSmartbarChat()` - opens Smartbar
* `SparaActions.openProductDemo()` - opens Product Demo
* `SparaActions.startVoiceCall()` - starts a voice call in the chat

`openChat` and `openSmartbarChat` accept the same optional arguments:

* `userMessage` - prefills and sends the lead's (i.e. user's) message
* `aiMessage` - starts the chat with a Spara-generated (i.e. AI) message

For example, `SparaActions.openChat({ userMessage: 'Hi' })` opens Spara Navigator with the user sending a message "Hi." See the [FAQ](#faq) for a full CTA-click walkthrough.

{% hint style="info" %}
On a page that embeds the full-page chat, `openChat()` and `startVoiceCall()` act on that chat rather than opening Navigator: `userMessage` is sent into the conversation already on the page, and a voice call starts inside it. `aiMessage` applies only while the conversation hasn't started yet — it can't replace an opening message the visitor has already seen. `setFields()` writes to the full-page chat's conversation the same way it does for Navigator and Smartbar.
{% endhint %}

#### How to open Product Demo from a link or email CTA

Add **`?spara_demo_open`** to any URL on a page that already loads the Spara script, and the Product Demo opens by itself when the visitor arrives. No JavaScript of your own is needed.

```
https://yoursite.com/?spara_demo_open
```

Use it in cold emails, LinkedIn messages, SMS campaigns, or anywhere else you send a link. It works alongside your other parameters, in any position:

```
https://yoursite.com/pricing?utm_source=email&spara_demo_open
```

The parameter opens the demo on **any** page that loads the Spara script, including pages you have excluded from the chat launcher — a link a recipient clicked is treated as an explicit request for the demo. While the demo is open, the chat launcher is hidden; it returns when the visitor closes the demo.

To open one specific demo instead of your account's default selection, give the parameter a value:

```
https://yoursite.com/?spara_demo_open=xyz123
```

The value can be either:

* An **agent ID** — opens that agent's Product Demo. If the agent runs multiple variants (A/B tests), traffic is split across them as configured. To find it, open the Product Demo agent in the Spara platform and click the folder icon in the header (next to the copy-link button).
* A **demo ID** — pins that one variant, ignoring the traffic split. It is the last segment of the public demo link, which the copy-link button on the Product Demo channel's page copies to your clipboard.

You do not need to say which kind you are using — Spara recognizes both. If the value matches neither, or the agent has no published Product Demo channel, Spara falls back to your account's published Product Demo channels, so the demo still opens.

{% hint style="info" %}
The parameter name is exactly `spara_demo_open`, in lowercase. The demo opens once per page load, so a visitor who closes it can reopen it by reloading the link.
{% endhint %}

#### Opening Product Demo from your own code

To open the demo from a button or your own logic, call `SparaActions.openProductDemo()`. With no argument it uses your account-wide selection; the first argument is an agent ID.

```javascript
SparaActions.openProductDemo("xyz123");
```

If your agent runs multiple Product Demo variants (A/B tests), you can pin one specific variant by passing its demo ID as a second argument. The demo ID is the last segment of the public demo link, which the copy-link button on the Product Demo channel's page copies to your clipboard:

```javascript
SparaActions.openProductDemo("xyz123", "abc456");
```

The demo ID works only together with the agent ID, and it does not fall back: if the demo ID is wrong or the variant is unpublished, the demo will not load. Leave it off unless you need to pin one specific variant.

{% hint style="info" %}
UTM parameters like `utm_source` and `utm_campaign` are automatically captured by Spara when the page loads, so you can combine the demo trigger with your usual campaign tracking (e.g., `https://yoursite.com/?demo&utm_source=newsletter`).
{% endhint %}

### How do I set up persistent ad tracking in Google Tag Manager?

To better attribute Spara conversions to your advertising campaigns (LinkedIn, Google, Facebook), you must capture and store "Click IDs" from the URL when a visitor first arrives.

A Click ID is a unique identifier that tracks a user’s click from an ad to your website.

Because users often browse multiple pages before engaging with the Spara AI agent, we cannot rely on the URL remaining the same. This guide shows you how to use **Google Tag Manager (GTM)** to save these IDs into a First-Party Cookie that follows the user for 90 days.

#### Supported Identifiers

This setup automatically handles the following industry-standard tracking parameters:

* gclid (Google Click ID)
* wbraid / gbraid (Google iOS Privacy-compliant IDs)
* li\_fat\_id (LinkedIn First-Party Tracking)
* fbclid (Facebook Click ID)

***

#### Step 1: The "Universal" Tracking Script

We will create a single tag that handles all major ad networks. This script checks the URL for any of the supported IDs and saves them as a secure cookie on your root domain.

1. Open Google Tag Manager.
2. Go to Tags > New.
3. Tag Type: Select Custom HTML.
4. Name: Script - Universal Ad Tracking.
5. HTML Content: Copy and paste the code block below.
6. Action Required: Edit the config.domain line to match your website's root domain (e.g., .spara.co).

```html
<script>
/**
 * SPARA.CO UNIVERSAL TRACKING SCRIPT
 * Captures ad parameters from URL and stores them in 90-day cookies.
 */
(function() {
    // --- CONFIGURATION ---
    var config = {
        domain: '.yourdomain.com', // REPLACE with your root domain (with leading dot)
        expiryDays: 90,            // Attribution window
        path: '/'
    };


    // Mapping: URL Parameter -> Cookie Name
    var idMap = {
        'gclid':     'gclid',
        'wbraid':    'wbraid',
        'gbraid':    'gbraid',
        'li_fat_id': 'li_fat_id',
        'fbclid':    'fbclid'
    };
    // ---------------------


    // Helper: Get Query Param
    function getParam(name) {
        var match = RegExp('[?&]' + name + '=([^&]*)').exec(window.location.search);
        return match && decodeURIComponent(match[1].replace(/\+/g, ' '));
    }


    // Helper: Set Cookie
    function setCookie(name, value) {
        var date = new Date();
        date.setTime(date.getTime() + (config.expiryDays * 24 * 60 * 60 * 1000));
        var expires = "expires=" + date.toUTCString();
        document.cookie = name + "=" + value + ";" + expires + ";domain=" + config.domain + ";path=" + config.path;
    }


    // Execution Logic
    // We loop through the map. If a param exists in the URL, we save/overwrite the cookie.
    // If it does NOT exist, we do nothing (preserving any existing cookie).
    for (var paramKey in idMap) {
        var paramValue = getParam(paramKey);
        if (paramValue) {
            setCookie(idMap[paramKey], paramValue);
        }
    }
})();
</script>
```

6. Triggering:
   1. Click Triggering.
   2. Select All Pages (or Initialization - All Pages).
   3. Why? This ensures that if a user lands on any page via an ad, the ID is captured immediately.
7. Save the tag.

***

#### Step 2: Create "Reader" Variables

Now that the cookies are being created, you need GTM variables to "read" them so you can pass the data to Spara or other tools.

1. Go to Variables.
2. Under User-Defined Variables, click New.
3. Variable Type: 1st Party Cookie.
4. Cookie Name: li\_fat\_id (Must match the name in the script map above).
5. URI Decode Cookie: ☑️Check this box.
6. Name: Cookie - li\_fat\_id.
7. Save.

Repeat this process for gclid, wbraid, and any others you need.

| Variable Name        | Cookie Name |
| -------------------- | ----------- |
| Cookie - gclid       | gclid       |
| Cookie - wbraid      | wbraid      |
| Cookie - li\_fat\_id | li\_fat\_id |

***

#### Step 3: Verification (QA)

Before publishing, verify the setup works using the GTM Preview mode or your browser's developer tools.

1. Visit your site with a fake ID:
2. <https://www.yourdomain.com/?li\\_fat\\_id=TEST\\_123\\&gclid=TEST\\_456>
3. Inspect the Cookies:
4. Right-click the page > Inspect.
5. Go to the Application tab (Chrome) or Storage tab (Firefox).
6. Expand Cookies on the left and select your domain.
7. Confirm the Data:
8. You should see li\_fat\_id with value TEST\_123.
9. You should see gclid with value TEST\_456.
10. Domain: Ensure it shows .yourdomain.com (this confirms it will work across subdomains).
11. Expires: Ensure the date is roughly 90 days in the future.

***

#### Step 4: Passing IDs to Spara

Once the variables are created, you can pass them into Spara via Spara's Javascript API.

If you are initializing Spara via GTM, update your Spara Initialization Tag to include these variables:

```javascript
// Example Spara Identification Call
Spara.identify({
  email: 'user@example.com',
  // Pass the GTM Variables we created in Step 2
  gclid: '{{Cookie - gclid}}', 
  li_fat_id: '{{Cookie - li_fat_id}}',
  wbraid: '{{Cookie - wbraid}}'
});
```

## FAQ

### How do I make a CTA button open Spara chat with a specific opening message?

A common pattern is to have a CTA on your page — for example, a "Request a Demo" button — that opens Spara Navigator with a page-specific opening message rather than the generic default. Use `SparaActions.openChat()` together with the `aiMessage` option to inject the opening message at click time.

For example, on a pricing page:

```javascript
// Example assumes CTA button has id="request-demo"
document.getElementById('request-demo')?.addEventListener('click', () => {
  SparaActions.openChat({
    aiMessage: "Happy to walk you through pricing — what plan are you considering?"
  });
});
```

This lets each page provide contextual messaging — a pricing page can lead with a pricing question, a features page can lead with a product question — instead of every CTA opening to the same generic greeting.

To make this work, Spara Navigator should be loaded in Preview Mode: Hidden, so the chat only appears when the CTA is clicked. See [Spara Navigator](https://docs.spara.com/agents/channels/chat/spara-navigator#preview-mode-hidden).


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.spara.com/developers/spara-api/javascript-api.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
