Skip to main content

Documentation

Deploy your Versiq conversion Agent on your site in minutes. Script tag or NPM, your choice.

What is a Versiq Agent?
A Versiq Agent is a conversational entity you configure in the portal: its business vertical, brand, conversion objectives and data sources. The widget is just how that Agent shows up on your site — a single line of code embeds it.
Versiq portal
configure your Agent
Agent
vertical · brand · objectives
widget
embed on your site
Visitor

Your data (catalogue · CRM · knowledge base) → Agent

Agent → events → Your CRM / analytics

The Agent lives in the portal

You configure it once (vertical, brand, objectives). The widget is its display channel, not the Agent itself.

Your data grounds the answers

Connect your sources (inventory, CRM, knowledge base): the Agent answers from your real data, not the public-web average.

The publishable key ties it together

A pk_* key binds the embedded widget to an Agent and its configuration, resolved at load time.

Choose your approach

5 minutes
Quick Integration
CDN script tag, no build required

Ideal for: WordPress, Shopify, static sites

See
30 minutes
Framework Integration
NPM package + TypeScript, full control

Ideal for: React, Next.js, Vue

See
0 minutes
Try First
Try the demo without installing

Ideal for: Discovery, evaluation

Try
Your first Agent in 5 minutes
From portal to first conversation, no build step.
  1. 1. Create the Agent in the portal

    In /app/portal, create an Agent: give it a name and pick its vertical (real estate, B2B…).

  2. 2. Grab a test key

    Generate a pk_test_* key from /app/portal/api-keys. It works on localhost with no domain configuration.

  3. 3. Embed the widget

    Paste the script tag (below) with your key into a locally-served page. No build step.

  4. 4. Converse

    Open the page: the Agent starts the conversation. Hook the ready or qualified event to react on your side.

Script Tag (CDN)
Add a single line of code to integrate the widget.
<!-- Add this script to your page -->
<!-- Production tip: pin a version + SRI, e.g. @0.2.0 + integrity="sha384-..." -->
<script
  src="https://unpkg.com/@dolard.eu/versiq-widget@latest/dist/widget.umd.js"
  data-api-key="pk_your_key"
></script>
Environments: dev → staging → prod
How to test the widget locally then roll it out to production without surprises. Each environment uses a different key and a dedicated domain declaration.

The widget intentionally splits two kinds of publishable keys. A test key (pk_test_*) is designed for local development: it always works on localhost, 127.0.0.1 and *.localtest.me, with zero domain configuration. A live key (pk_live_*) is designed for public environments: it refuses localhost and requires an explicit domain whitelist.

1. Local development

  • Key: pk_test_*
  • Domain: no declaration required — test keys always allow localhost, 127.0.0.1 and *.localtest.me.

Create a test key from /app/portal/api-keys, embed the widget on http://localhost:3000 (or your dev server port), and exercise every feature without touching the Domains page.

2. Staging / preview

  • Key: pk_live_* (dedicated to staging)
  • Domain to declare: https://staging.mysite.com (exact URL of your preview environment)

Create a live key dedicated to staging — never reuse the production key. Add the exact URL of your preview environment in /app/portal/domains. If your staging runs on a custom port (e.g. preview-42.mysite.com:8443), add the full URL with port.

3. Production

  • Key: pk_live_*
  • Domain to declare: https://mysite.com (or wildcard *.mysite.com to cover several subdomains at once)

For a site that serves several subdomains (app, www, checkout…), a *.mysite.com wildcard is more maintainable than multiple exact entries. Note: the wildcard only covers direct subdomains — add mysite.com as an extra entry if the apex domain also serves the widget.

Why split test and live?

A pk_live_* live key refuses localhost even in dev and requires a domain whitelist. This is intentional: if a live key is stolen (leak in a public GitHub repo, compromised browser extension, accidental screenshot), it cannot be used from a local HTML file or from any undeclared third-party site. An attacker who gets the key cannot do anything with it without also controlling one of the domains you explicitly allowed. This is the seat belt that compensates for the key being publicly exposed in your pages' HTML.

Wildcard syntax

A *.mysite.com wildcard matches app.mysite.com, www.mysite.com, checkout.mysite.com — but not mysite.com itself. To cover the apex domain, add it as a separate entry. Wildcards only support one level (*.mysite.com ≠ **.mysite.com): a.b.mysite.com is not matched, you need to add *.b.mysite.com as well.

What happens if the domain is not allowed?

The Versiq server returns a 403 ORIGIN_NOT_ALLOWED error and the widget stays silent (no chat window opens). Enable the widget's debug option during integration to see the response in the console — it is usually the first thing to check when the widget fails to appear in staging.

NPM + TypeScript
NPM package + TypeScript, full control
npm install @dolard.eu/versiq-widget
import { createWidget } from "@dolard.eu/versiq-widget";

const widget = createWidget({ apiKey: "pk_your_key" });

// Control the widget
widget.open();
widget.close();
widget.reset();
Configuration
Customize widget behavior with data-attributes. Theme, position and language are now configured from the Versiq portal and resolved at load time via the publishable key.
OptionTypeDefaultDescription
data-api-keystringrequiredPublishable API key (pk_*) — required, identifies your Agent
data-containerstring-CSS selector for the inline mode container (e.g., '#chat'). The programmatic SDK also accepts a raw HTMLElement.
data-base-urlstringProduction URLBase URL for the embed
data-debugbooleanfalseEnable debug logging
data-emailstring-Pre-identified visitor email (HMAC verification)
data-user-idstring-Host-side unique user identifier (HMAC verification)
data-user-hashstring-HMAC-SHA256 of the userId (or email as fallback), signed with the identity secret
Real-world Examples
Discover integration scenarios adapted to your context.
Real estate showcase site
Floating widget + events → qualified lead by email
ApproachScript tag
ResultComplete buyer profile sent to your CRM
Next.js B2B Application
NPM + TypeScript + CRM webhook
ApproachNPM Package
ResultType-safe integration with Pipedrive/HubSpot
E-commerce Marketplace
Inline mode + custom styling
ApproachInline
ResultWidget embedded in your existing design
Display Modes
Choose between a floating or embedded widget.

Floating

The widget appears as a floating button in a corner of the page. This is the default mode — no HTML option required, position is set in /app/portal/widget.

Inline

The widget is embedded directly into a container on your page. Ideal for a contact page or dedicated landing page. Pass a CSS selector via data-container, or an HTMLElement via the programmatic SDK.

import { createWidget } from "@dolard.eu/versiq-widget";

// container accepts a CSS selector OR an HTMLElement
const widget = createWidget({
  apiKey: "pk_your_key",
  container: "#chat-container",
});
Multiple Agents on one page
Run multiple widgets on the same page — each one a distinct Agent with its own vertical.

Script Tags

<!-- B2B qualification widget -->
<script
  src="https://unpkg.com/@dolard.eu/versiq-widget@latest/dist/widget.umd.js"
  data-api-key="pk_your_b2b_key"
></script>

<!-- Real estate widget (same page) -->
<script
  src="https://unpkg.com/@dolard.eu/versiq-widget@latest/dist/widget.umd.js"
  data-api-key="pk_your_immo_key"
></script>

Programmatic API

import { createWidget } from "@dolard.eu/versiq-widget";

// Each API key maps to an Agent with its own vertical, theme, and
// position — all configured from the admin portal.
const b2bWidget = createWidget({ apiKey: "pk_your_b2b_key" });
const immoWidget = createWidget({
  apiKey: "pk_your_immo_key",
  // container pins the widget to a specific DOM node (inline mode).
  container: document.getElementById("demo-immo"),
});

// Access instances after they're ready
b2bWidget.on("ready", () => {
  // window.VersiqApps.get(b2bWidget.applicationId) → VersiqAPI
  console.log("B2B widget ready:", b2bWidget.applicationId);
});
Theming
Match the widget appearance to your brand.

Theming is configured in the portal

Since release 2024.11, theming (colors, typography, border radius, light/dark scheme, position) is managed from /app/portal/widget. The widget resolves these settings at load time via the publishable key — no HTML changes required.

Open portal

Toggle light/dark at runtime

To sync the widget's color scheme with your host site (user toggle, system preference), use the JavaScript API window.Versiq.setColorScheme('light' | 'dark' | 'auto').

Data Sources
Connect your product or service data to power the AI conversation. Without a data source, the conversion agent performs conversational profiling.

Versiq connects to your data via a generic interface. Each source declares its capabilities, and AI tools adapt automatically.

DataSource Interface

Implement this TypeScript interface to connect your own data source.

interface DataSource {
  readonly id: string;
  readonly displayName: string;
  readonly coverageDescription: string;

  getCapabilities(): ReadonlySet<DataSourceCapability>;

  search?(params: Record<string, unknown>): Promise<unknown[]>;
  getStats?(zone: string): Promise<Record<string, unknown>>;
  getAreaMetrics?(zone: string, filters: Record<string, unknown>): Promise<Record<string, unknown>>;
  estimate?(params: Record<string, unknown>): Promise<Record<string, unknown>>;
  count?(areas: string[], filters: Record<string, unknown>): Promise<number>;
  checkCoverage?(zone: string): Promise<{ covered: boolean; message?: string }>;
}

Capabilities

Each capability enables one or more AI tools in the conversation.

CapabilityMethodToolsDescription
searchsearch()searchPropertiesSearch items/products with filters
statsgetStats()getCityStatsAggregated statistics by zone
areaMetricsgetAreaMetrics()getAreasOverviewDetailed metrics by zone (overview)
estimateestimate()estimateProperty, estimateRentPrice or value estimation
countcount()getPropertyCountCount with filters
coveragecheckCoverage()checkZoneCoverageZone coverage verification

Example: REST API

A concrete example connecting to a third-party REST API.

class MyApiDataSource implements DataSource {
  readonly id = "my-api";
  readonly displayName = "Mon API Produits";
  readonly coverageDescription = "Catalogue produit via API REST";

  getCapabilities() {
    return new Set(["search", "count"]);
  }

  async search(params) {
    const res = await fetch(`https://api.example.com/products?${qs(params)}`);
    return res.json();
  }

  async count(areas, filters) {
    const res = await fetch("https://api.example.com/products/count", {
      method: "POST",
      body: JSON.stringify({ areas, filters }),
    });
    const data = await res.json();
    return data.count;
  }
}

Graceful Degradation

When a source only supports certain capabilities, unsupported tools are automatically hidden. Partners don't need to configure anything on the tools side.

search
stats
estimate
count
areaMetrics
coverage

Access modes

Versiq adapts to your infrastructure. The custom mode (DataSource) is available today; native connectors and standard protocols are on the way.

Native Integrations: pre-built connectors (CRM, e-commerce, etc.)Soon
Custom Mode (DataSource): TypeScript interface to implementAvailable
MCP (Model Context Protocol): expose your data via an MCP serverSoon
A2A (Agent-to-Agent): inter-agent communication for third-party systemsSoon
Identity Verification
When visitors are already logged in on your site, silently verify their identity via HMAC-SHA256 — zero friction, verified leads.

When to use

Identity verification is designed for authenticated areas where visitors are already logged in — SaaS dashboards, client portals, member areas. It is not intended for public marketing pages where visitors are anonymous.

Identity Levels

OptionTypeDefaultDescription
AnonymousCookie / session ID-Default for all visitors — no setup required
IdentifiedEmail shared in chat-Visitor shares their email during conversation
VerifiedHMAC attested by host-Host site cryptographically attests the visitor's identity — zero friction

Step 1: Compute HMAC server-side

The HMAC must be computed on your backend. Never expose the secret in client-side code.

Node.js

import crypto from "node:crypto";

// Sign the stable userId when you have one (recommended), else the email.
// Send both data-user-id and data-user-hash; data-email stays optional.
const signedValue = user.id ?? user.email;
const hmac = crypto
  .createHmac("sha256", process.env.VERSIQ_IDENTITY_SECRET)
  .update(signedValue)
  .digest("hex");

Python

import hmac
import hashlib
import os

# Sign the stable user id when you have one (recommended), else the email.
# Send both data-user-id and data-user-hash; data-email stays optional.
signed_value = user.id or user.email
user_hash = hmac.new(
    os.environ["VERSIQ_IDENTITY_SECRET"].encode(),
    signed_value.encode(),
    hashlib.sha256,
).hexdigest()

Step 2: Pass identity to the widget

Add the identity attributes to your widget script tag, or use the programmatic API after login.

Script tag

<script
  src="https://unpkg.com/@dolard.eu/versiq-widget@latest/dist/widget.umd.js"
  data-api-key="pk_live_..."
  data-email="${user.email}"
  data-user-id="${user.id}"
  data-user-hash="${hmac}"
></script>

Dynamic (post-login)

For single-page apps, identify the visitor after login without reloading the page.

// After user logs in, identify them dynamically
window.Versiq.identify({
  email: "user@example.com",
  userId: "usr_123",
  userHash: "<computed_hmac>",
});

HMAC Input

The HMAC is computed on userId if provided (stable identifier, recommended), or email as fallback. Using userId means the HMAC stays valid even if the user changes their email.

Security

The identity secret must never appear in client-side code. Generate it in your Versiq admin dashboard under Settings > Identity Verification.

Events
React to user interactions with events.
OptionTypeDefaultDescription
ready--Widget is loaded and ready
open--Widget was opened
close--Widget was closed
messageWidgetMessage-New message in the conversation
profile-updateWidgetProfile-User profile was updated
qualified{ profile, score }-Lead qualified with relevance score
quota-warning{ remaining, limit }-API quota is running low
quota-exceeded--API quota has been exceeded
error{ code, message }-An error occurred
identity-verified{ email, userId? }-Visitor identity was verified via HMAC

Usage example

// Subscribe to events
window.Versiq.on("ready", () => {
  console.log("Agent is ready");
});

window.Versiq.on("profile-update", (data) => {
  console.log("Profile updated:", data.profile);
});

window.Versiq.on("qualified", (data) => {
  console.log("Lead qualified:", data.profile);
  console.log("Score:", data.score);
  // Send to your CRM
});

window.Versiq.on("identity-verified", (data) => {
  console.log("Identity verified:", data.email);
});

window.Versiq.on("quota-warning", (data) => {
  console.warn("Quota low:", data.remaining, "/", data.limit);
});

window.Versiq.on("error", (data) => {
  console.error("Error:", data.code, data.message);
});
JavaScript API
Control the widget programmatically via window.Versiq.
OptionTypeDefaultDescription
open()void-Open the widget
close()void-Close the widget
reset()void-Reset the conversation
setTheme(theme)void-Change theme dynamically
setColorScheme(scheme)void-Toggle the color scheme ('light' | 'dark' | 'auto') — handy for wiring the host site's own theme switch
on(event, handler)void-Subscribe to an event
off(event, handler)void-Unsubscribe from an event
destroy()void-Remove widget and cleanup resources
identify(params)void-Identify a visitor with HMAC verification
versionstring-Current SDK version (read-only)
applicationIdstring""Agent ID (applicationId) resolved after load (empty until ready is emitted)
window.VersiqAppsMap<string, VersiqAPI>-Global map of widget instances — key: applicationId, value: VersiqAPI