---
title: API reference
description: The window.handhold SDK. Open demos with fields and UTM attribution, fire programmatic triggers, and control the launcher.
slug: api
order: 2
---

Every method lives on `window.handhold`, available once the `embed.js` script from the [quick start](/docs) has loaded.

## `openDemo(options)`

Opens a Demo or Inbound Q&A agent in an overlay. To open an Onboarding agent from code, prefer a [programmatic trigger](#launch-programmatic-triggers), which carries context the agent can act on.

```javascript
/**
 * Opens a demo with the specified configuration.
 *
 * @param options.id (required) - Agent ID (a UUID; copy it from the agent's Embed dialog)
 * @param options.modality (optional, default: 'TEXT') - 'VOICE' | 'TEXT'. Voice lets visitors talk to the agent.
 * @param options.fields (optional) - Field values keyed by Handhold field ID
 * @param options.utm (optional) - UTM attribution: source, medium, campaign, term, content
 * @param options.externalVisitorId (optional) - Your identifier for the visitor, so returning visitors are recognised across devices
 * @param options.theme (optional, default: host page or 'light') - 'light' | 'dark'
 * @param options.locale (optional, default: 'en') - Language code (see supported languages)
 *
 * @example
 * window.handhold.openDemo({
 *   id: 'agent-uuid',
 *   modality: 'VOICE',
 *   externalVisitorId: 'user-8f3k2p',
 *   fields: {
 *     '123e4567-e89b-12d3-a456-426614174000': 'Enterprise',
 *   },
 *   utm: {
 *     source: 'google',
 *     medium: 'paid-search',
 *     campaign: 'spring-launch',
 *     term: 'security-platform',
 *     content: 'hero-cta',
 *   },
 *   locale: 'en',
 * });
 */
```

`fields` is keyed by Handhold field ID, not field name. Values must match the configured field type (`string`, `number`, or `boolean`); select fields use one of their configured options. The fields must be configured on the agent being opened. Empty string values are ignored.

`utm` supports the canonical keys `source`, `medium`, `campaign`, `term`, and `content`. Values passed here override matching UTM values from the page URL or an earlier browser session for the new session. All five UTM values are available as mapping sources in Handhold automations, including HubSpot contact mappings.

`externalVisitorId` ties the session to your own identifier for the visitor. When the same identifier is passed again, on any device or browser, Handhold recognises the visitor: the start screen is prefilled with previously collected answers and the agent carries context from earlier conversations. Identifiers must be between 8 and 255 characters and are scoped to your organisation.

**Important:** treat the identifier like a key to the visitor's conversation history. Whoever presents the same value is recognised as that visitor, including their previously shared answers and past conversations. Use an opaque value such as an internal user id or a random key, never an email address or a guessable sequence, and only pass it on pages where the visitor is signed in.

The `locale` option accepts any of the [supported languages](/docs/languages).

For Inbound Q&A agents, `openDemo` additionally accepts `initialQuery`: pass the visitor's question from your own search bar or help input and the agent opens already answering it. This requires the agent's iframe from the [quick start](/docs) to be on the page.

To start with the launcher hidden, pass both `mode: 'hidden'` and `hidden: true`, then reveal it later with `showLauncher()`. (The `launcher` and `input` modes are iframe URL parameters, shown in the quick start, not `openDemo` options.)

## `launch()` (programmatic triggers)

Programmatic triggers are the recommended way to open an Onboarding agent from your own code. Instead of just popping the agent open, a trigger carries the context you configured, so the agent knows why it appeared and greets the visitor accordingly: a help button on a settings page, a "show me" link in an announcement, a moment where the visitor seems stuck.

Set one up in the agent's **Triggers** tab: create a **Trigger from your code** trigger, give it a name and, optionally, context the agent should know when it fires. The dialog gives you a ready-made snippet:

```javascript
window.handhold.launch({ agentId: 'YOUR_AGENT_ID', triggerId: 'YOUR_TRIGGER_ID' });
```

Call it on any page that has both the `embed.js` script and the agent's iframe from the [quick start](/docs) installed.

- The call only fires when the trigger is configured on the agent. An unknown or removed trigger is silently ignored: nothing opens and no error is shown. This means you can ship trigger calls in your product ahead of configuring them in Handhold, and turn each one on by creating it in the Triggers tab.
- Opens the agent and starts a session. If the agent is already open, the call does nothing.
- Safe to call while the widget is still initializing: as long as the agent's iframe is on the page, early calls run once it's ready.

## `showLauncher(agentId?)`

Shows the Onboarding agent launcher. Only applies to **Onboarding** agents; it has no effect on Demo agents. If the user is in an active conversation, this is a no-op (the conversation UI remains visible).

```javascript
/**
 * @param agentId (optional) - Agent identifier. If omitted, targets the most recently opened agent.
 *
 * @example
 * window.handhold.showLauncher('agent-uuid');
 */
```

## `hideLauncher(agentId?)`

Hides the Onboarding agent launcher. Only applies to **Onboarding** agents; it has no effect on Demo agents. If the user is in an active conversation, this is a no-op (the conversation stays open).

```javascript
/**
 * @param agentId (optional) - Agent identifier. If omitted, targets the most recently opened agent.
 *
 * @example
 * window.handhold.hideLauncher('agent-uuid');
 */
```
