@askdialog/dialog-sdk
v2.9.0
Published
Dialog SDK
Downloads
5,535
Readme
Dialog SDK
Dialog
Dialog is an AI assistant designed to boost e-commerce sales by providing intelligent product recommendations and seamless customer interactions.
Visit our website: Dialog AI Assistant
Description
Dialog SDK is a powerful TypeScript library that seamlessly integrates the Dialog AI assistant into your applications. It provides a comprehensive set of tools for managing assistant interactions, handling e-commerce operations like product fetching and cart management, and customizing the assistant's appearance to match your brand.
Get started
Prerequisites
Before using the Dialog SDK, you need:
- An active API Key, you can retrieve your api key in your organization settings
Installation
npm install @askdialog/dialog-sdk
# or
pnpm add @askdialog/dialog-sdk
# or
yarn add @askdialog/dialog-sdkYou can also use our CDN link if you’re not using a package manager.
- Add the script to your project (replace X, Y, Z by versions)
<script src="https://d2m6yt8rnm4dos.cloudfront.net/dialog-sdk.X.Y.Z.min.js"></script>- The
DialogSDKobject will be available on thewindow. You can access all features as shown below:
const client = new window.DialogSDK.Dialog({
apiKey: 'YOUR_API_KEY',
// ........
})Instantiate the client
import { Dialog } from '@askdialog/dialog-sdk';
const client = new Dialog({
apiKey: 'YOUR_API_KEY', // required
locale: 'TARGETED_LOCALE', // required
countryCode: 'FR', // optional, ISO 3166 alpha-2
callbacks: {
addToCart: async ({
productId,
quantity,
currency,
variantId,
price,
}: {
productId: string;
quantity: number;
currency?: string;
variantId?: string;
price?: string;
}) => Promise<void>, // required
getProduct: async (
productId: string,
variantId?: string
) => Promise<SimplifiedProduct>, // required
},
});The apiKey is required to authenticate with our API and interact with our assistant. The locale specifies the language you want to use.
The optional countryCode (ISO 3166 alpha-2, e.g. 'FR', 'US') sets the region used to format prices (separators, symbol placement) and the language variant. It does not change the currency itself, which comes from each product. The effective region is resolved in this order:
- the
countryCodeparameter, when provided; - the region embedded in
locale(e.g.'en-US'→US); - the region guessed from the language (e.g.
'fr'→FR).
The addToCart function is triggered when a user clicks the AddToCart button. The getProduct function is used to display product information in the assistant.
callbacks is optional: a search-only integration can construct the client with just apiKey and locale. Only getProduct() and addToCart() require their callback — they throw an explicit configuration error when it is absent; every other feature works without callbacks.
When the client is instantiated, it will automatically insert into the DOM the Dialog Assistant script, so you can interact with the assistant using sendProductMessage or sendGenericMessage. This assistant runtime always loads — it owns the shopper identity, consent handling and analytics bridge — while the heavy assistant UI stays lazy-loaded and is not fetched eagerly.
OneTrust auto-blocking
If your site uses OneTrust auto-blocking, it may neutralize the assistant script injected by the SDK (type rewritten to text/plain) for visitors who declined cookies — a data-ot-ignore on your own SDK <script> tag does not cover dynamically injected scripts. Two remedies, both merchant-side decisions:
- categorize the assistant CDN domain as strictly necessary in your OneTrust console, or
- pass the optional
ignoreOneTrustAutoBlock: trueconstructor flag so the SDK addsdata-ot-ignoreon the script it injects.
Either is compliance-safe with regard to analytics: the assistant gates all analytics on consent internally and sends nothing without an explicit opt-in (inspect window.dialog.audit.consent).
Disabling add-to-cart
Some sessions must hide purchasing actions — for example a B2B storefront that hides its own add-to-cart when a B2B customer is logged in. Pass the optional disableAddToCart: true constructor flag (type boolean, default false) to suppress Dialog add-to-cart for that widget instance/session:
new Dialog({
apiKey: 'YOUR_API_KEY',
locale: 'fr',
disableAddToCart: true, // hide the add-to-cart CTA for this session
});When set:
- the assistant hides/disables the add-to-cart CTA on both product recommendation cards and conversational product cards;
client.addToCart(...)becomes a no-op — it never invokes yourcallbacks.addToCartand emits noTRACK_ADD_TO_CARTevent, so a stale UI cannot add to the cart or pollute analytics;- product links and recommendation browsing are unaffected.
Omit the flag (or set it to false) to keep the default behavior — the add-to-cart CTA and analytics are unchanged.
Getters
- apiKey
- theme
- userId
- locale
Features
- Send a message with context
client.sendProductMessage({
question: 'YOUR_QUESTION', // required
productId: 'PRODUCT_ID', // required
productTitle: 'PRODUCT_TITLE', // required
answer: '', // Optional
selectedVariantId?: 'CURRENT_VARIANT_ID', // Optional
})- Send message without context
client.sendGenericMessage({
question: 'YOUR_QUESTION', // required
});- Get locale information
const localizationInfos = await client.getLocalizationInformations();
/*
Example of expected result when locale: 'en'
{
countryCode: "US",
formatted: "en-US",
language: "English",
locale: "en"
}
*/- Get suggestion questions
You can use this query to make your own integration and trigger sendProductMessage or sendGenericMessage on user click.
const suggestions = await client.getSuggestions(productId);
/*
Example of expected result:
{
"questions": [
{
"question": "What is the formula used in this repairing gel to soothe the skin after sun exposure?"
},
{
"question": "How does this gel relieve sunburn and reduce pain?"
},
{
"question": "What are the benefits for the skin after using this product following excessive exposure to UV rays?"
}
],
"assistantName": "Your expert",
"inputPlaceholder": "Ask any question...",
"description": "Ask any question about this product"
}
*/- Search products
client.search() performs a typed product search through Dialog's public API, with no framework and no commerce callbacks required.
import { Dialog, DialogSearchError } from '@askdialog/dialog-sdk';
import type { SearchResponse } from '@askdialog/dialog-sdk';
const client = new Dialog({ apiKey: 'YOUR_API_KEY', locale: 'fr' });
const response: SearchResponse = await client.search({
query: 'shampoo',
page: 0, // optional, zero-indexed (default 0)
hitsPerPage: 20, // optional, 1-100 (default 20)
queryId: previousResponse?.queryId, // optional, resend while the query is unchanged
});
// response.hits[n].product: { id, title?, url?, imageUrl?, priceRange?, inStock? }With the IIFE bundle the results are plain runtime JSON (same shape, no types):
<script src="https://d2m6yt8rnm4dos.cloudfront.net/dialog-sdk.X.Y.Z.min.js"></script>
<script>
const client = new window.DialogSDK.Dialog({ apiKey: 'YOUR_API_KEY', locale: 'fr' });
client.search({ query: 'shampoo' }).then((response) => console.log(response.hits));
</script>A non-2xx answer rejects with DialogSearchError — stable name, HTTP status, optional machine-readable code (e.g. SEARCH_INDEX_NOT_FOUND) and message. Aborting rejects with the native AbortError, and network failures keep their native errors.
client.search() itself is stateless: no debounce, no cache, no automatic cancellation of previous searches. For search-as-you-type, use the search controller below instead of hand-rolling those.
- Search controller
createSearchController() wraps the stateless transport with the stateful behavior every search UI needs — debounce (immediate on explicit submission), cancellation of the in-flight request, stale-response protection (a late response never replaces newer results, even if the transport ignores the abort), pagination that resets on a new query, idle / loading / success / empty / error states, retry, and the DEC-2448 attribution events (view_search_results viewport impressions, select_search_result clicks) with a consistent query_id. It has no framework or rendering dependency: raw JavaScript, React, Vue and Shopify integrations are rendering-and-routing adapters around it.
import { createSearchController, Dialog, SearchStatus } from '@askdialog/dialog-sdk';
const client = new Dialog({ apiKey: 'YOUR_API_KEY', locale: 'fr' });
const controller = createSearchController({
search: (request, options) => client.search(request, options),
analytics: {
surface: 'search_page', // where results are displayed
trackViewSearchResults: (params) => client.trackViewSearchResults(params),
trackSelectSearchResult: (params) => client.trackSelectSearchResult(params),
},
navigate: (url) => router.push(url), // optional platform routing adapter
debounceMs: 250, // optional (default 250)
hitsPerPage: 12, // optional (default 12)
});
const unsubscribe = controller.subscribe((state) => {
// state: { status, query, page, response?, error? }
if (state.status === SearchStatus.SUCCESS) {
renderCards(state.response.hits).forEach((element, index) => {
controller.observeResult(element, index); // viewport impression
element.onclick = () => controller.selectResult(index); // attribution, then `navigate`
});
}
});
input.oninput = () => controller.setQuery(input.value); // debounced
form.onsubmit = () => controller.submit(input.value); // immediate
nextButton.onclick = () => controller.setPage(controller.getState().page + 1);
retryButton.onclick = () => controller.retry();
// On teardown (SPA unmount): cancel in-flight work and detach observers.
controller.dispose();The adapter contract for a framework binding (React, Vue, Shopify):
- Rendering — subscribe to the controller (
subscribe/getStatefit React'suseSyncExternalStoreand a VueshallowRefupdated by the listener) and render the five states; never re-implement debounce,AbortControlleror race protection locally. - Attribution — call
observeResult(element, index)for every rendered result andselectResult(index)on every result click (including middle-click/cmd+click). Do notpreventDefaulta same-tab navigation: attribution is recorded first and the events survive it. - Routing — platform navigation and URL synchronization (query params, history) stay in the adapter: pass
navigatefor router-driven platforms, or let plain<a href>links navigate natively. - Lifecycle — create one controller per search surface and
dispose()it on unmount.
The raw JavaScript reference adapter lives in packages/search-example.
- Handler for fetch product
The getProduct callback is called by the assistant to display product information. You must return an object matching the SimplifiedProduct interface.
Parameters:
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| productId | string | Yes | The product identifier |
| variantId | string | No | The selected variant identifier |
Return type: SimplifiedProduct
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| id | string | Yes | Product identifier |
| title | string | Yes | Product name |
| handle | string | Yes | URL-friendly product slug |
| totalInventory | number | Yes | Total available stock across all variants |
| variants | SimplifiedProductVariant[] | Yes | List of product variants (see below) |
| descriptionHtml | string | No | Product description in HTML |
| url | string | No | Product page URL |
| featuredImage | { url?: string } \| null | No | Main product image |
| options | SimplifiedProductOption[] | No | Product options (size, color, etc.) |
Variant: SimplifiedProductVariant
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| id | string | Yes | Variant identifier |
| price | string | Yes | Variant price (e.g. "29.99") |
| currencyCode | string | Yes | ISO 4217 currency code (e.g. "EUR", "USD") |
| displayName | string | No | Variant display name |
| inventoryQuantity | number | No | Available stock for this variant |
| compareAtPrice | string \| null | No | Original price before discount |
| url | string | No | Variant-specific page URL |
| selectedOptions | { name: string; value: string }[] | No | Option values for this variant (e.g. [{ name: "Size", value: "M" }]) |
| image | { url?: string } \| null | No | Variant-specific image |
Option: SimplifiedProductOption (optional)
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| id | string | Yes | Option identifier |
| name | string | Yes | Option name (e.g. "Size", "Color") |
| position | number | Yes | Display order |
| values | string[] | Yes | Available values (e.g. ["S", "M", "L"]) |
Example:
const client = new Dialog({
...,
callbacks: {
getProduct: async (
productId: string,
variantId?: string,
): Promise<SimplifiedProduct> => {
const response = await fetch(`https://your-api.com/products/${productId}`);
const data = await response.json();
return {
id: data.id,
title: data.name,
handle: data.slug,
totalInventory: data.stock,
featuredImage: { url: data.imageUrl },
variants: data.variants.map((v: any) => ({
id: v.id,
price: v.price.toString(),
currencyCode: 'EUR',
displayName: v.name,
inventoryQuantity: v.stock,
compareAtPrice: v.originalPrice?.toString() ?? null,
selectedOptions: v.options,
image: v.imageUrl ? { url: v.imageUrl } : null,
})),
options: data.options?.map((o: any) => ({
id: o.id,
name: o.name,
position: o.position,
values: o.values,
})),
};
},
},
});- Handler for add to cart
const client = new Dialog({
...,
callbacks: {
addToCart: ({
productId,
quantity,
variantId,
currency
}: {
productId: string;
quantity: number;
currency?: string;
variantId?: string;
}): Promise<void> => {
// Call your api to trigger addToCart
const response = await fetch('....');
// Trigger other stuff like confirmation modal
return;
}
},
});Theming (Still in construction)
We are currently working on the theming part so you may find some issues. Contact us if you need more customization.
⚠️ Title, description and content properties are used only to theme the Vue component for the moment.
const client = new Dialog({
...,
theme: {
backgroundColor?: string;
primaryColor?: string;
ctaTextColor?: string;
ctaBorderType?: 'straight' | 'rounded';
capitalizeCtas?: boolean;
fontFamily?: string;
highlightProductName?: boolean;
title?: { // Used in Vue component only
fontSize?: string;
color?: string;
}
description?: { // Used in Vue component only
fontSize?: string;
color?: string;
}
content?: { // Used in Vue component only
fontSize?: string;
color?: string;
}
}
});Tracking
Our SDK includes a tracking system to monitor user interactions in your purchase flow.
Automatic Tracking
When a user interacts with our assistant and clicks on an "Add to Cart" CTA, it automatically triggers the previously configured addToCart callback (see "Client Instantiation" section). These events are tracked internally by our system.
Manual Tracking
However, we cannot automatically detect cart additions or checkout completions that occur after using our assistant. To get accurate data in your Dialog dashboards, you should use the following tracking methods:
Available Methods
client.registerAddToCartEvent({
productId: 'ProductIdentifier', // {string} - Required
quantity: 1, // {number} - Required
currency: 'EUR', // {string} - Optional
variantId: 'VariantIdentifier', // {string} - Optional
price: '12.00' // {string} - Optional
});
// Checkout is order-level: call ONCE per completed order with the order total.
// `orderValue` is what the "Revenue generated" dashboard reads.
// Do NOT call this per line item — without an order total, revenue resolves to 0.
client.registerSubmitCheckoutEvent({
orderValue: 59.98, // {number} - Required - the order total
currency: 'EUR', // {string} - Optional
transactionId: 'OrderIdentifier', // {string} - Optional - order id, used to de-duplicate reloads
items: [ // {array} - Optional - line items, for product-level attribution only
{ productId: 'ProductIdentifier', quantity: 1, price: 29.99, variantId: 'VariantIdentifier' },
],
});
// Deprecated per-line signature — still accepted for backward compatibility,
// but carries no order total so revenue cannot be computed. Prefer the call above.
client.registerSubmitCheckoutEvent({
productId: 'ProductIdentifier',
quantity: 1,
price: '12.00',
currency: 'EUR',
});Listen for Assistant Events
The SDK provides real-time event listening for user interactions with the Dialog assistant.
// Basic event listener setup
const unsubscribe = client.onAssistantEvent((event) => {
console.log('Event type:', event.type);
console.log('Event payload:', event.payload);
});
// Clean up the event listener when needed
unsubscribe();Event Structure
All events follow this structure:
interface AssistantEvent {
type: string;
payload: {
// Common fields (included in all events)
date: string; // ISO timestamp
locale: string; // Current locale
url: string; // Current page URL
userId?: string; // User ID if available
// Event-specific fields
productId?: string; // When interacting with products
variantId?: string; // When interacting with variants
}
}Available Event Types
- userOpenedAssistant - User opened the assistant interface
- userClosedAssistant - User closed the assistant interface
- userSentMessage - User sent a message to the assistant
- userClickedOnProductCard - User clicked on a product card for more details
- userOpenedRecommendation - User clicked on a product recommendation
- userAddedToCart - User added a product to cart via the assistant
- userSendPositiveFeedback - User gave positive feedback on AI response
- userSendNegativeFeedback - User gave negative feedback on AI response
Example Usage
client.onAssistantEvent((event) => {
switch (event.type) {
case 'userAddedToCart':
// Track conversion in your analytics
analytics.track('assistant_conversion', {
productId: event.payload.productId,
timestamp: event.payload.date
});
break;
case 'userSendNegativeFeedback':
// Log for improvement analysis
console.log('Negative feedback at:', event.payload.url);
break;
}
});