Widget

Widget Embedding Guide

The <salesbooth-deal> web component handles the complete deal flow. This guide covers all 27 attributes, theming, events, and advanced features.

Quick Embed

The minimum required setup — just your publishable API key and a product ID:

<script src="https://salesbooth.com/sdk/v1/salesbooth-widget.js"></script> <salesbooth-deal api-key="sb_pub_your_key" products="prod_a1b2c3d4" ></salesbooth-deal>
Publishable keys are safe for client-side use

Keys prefixed sb_pub_ have limited read-only scope. They can list products and submit deals, but cannot read customer data or manage your account.

All Widget Attributes

The widget is configured entirely via HTML attributes. All attributes except api-key are optional.

Authentication

AttributeTypeDescription
api-key required string Your publishable API key (sb_pub_ prefix). Never use a secret key here.

Product Configuration

AttributeTypeDefaultDescription
products string Comma-separated product IDs. If omitted, customer selects from your full catalog.
product-selection string "single" Product selection mode. "single" or "multi".
saved-config-id string Pre-load a saved product configuration by ID. Skips the configuration step.
currency string "USD" ISO 4217 currency code. Customer sees prices in this currency.

Content & Copy

AttributeTypeDefaultDescription
title string "Complete Your Deal" Heading shown at the top of the widget.
cta-text string "Get Started" Text on the primary call-to-action button.
locale string "en" Language code for UI strings. Built-in: en, fr, de, es, ja, pt, zh.
locale-url string URL to a custom translations JSON file. Overrides the built-in locale strings.

Branding & Appearance

AttributeTypeDefaultDescription
theme-color string "#2563eb" Primary brand color (hex or CSS color). Used for buttons, progress, and accents.
dark-mode boolean false Enable dark theme. Set to "true" or "auto" to match system preference.
font-family string system-ui CSS font-family string. Applied to all widget text.
border-radius string "8px" Border radius for cards and buttons. E.g. "0", "4px", "16px".
logo-url string URL to your logo image. Shown in the widget header. Recommended: 32px height.
custom-css string URL to a CSS file injected into the widget's shadow DOM. For advanced customization.
button-style string "filled" Button variant: "filled", "outlined", or "ghost".

Customer Pre-fill

AttributeTypeDescription
customer-name string Pre-fill the customer name field. Useful for authenticated users.
customer-email string Pre-fill the customer email field.
customer-phone string Pre-fill the customer phone field.

Workflow & Steps

AttributeTypeDefaultDescription
steps number auto Override the number of checkout steps (1–8).
contract-template string Contract template ID. If set, the contract step is shown with this template.
signature-mode string "draw" Signature capture mode: "draw" or "checkbox".

Analytics & Tracking

AttributeTypeDefaultDescription
analytics boolean true Enable session replay and funnel analytics. Set to "false" to disable.
analytics-consent boolean true Whether the user has given analytics consent. Set to "false" to suppress tracking.
analytics-callback string Global function name called with raw analytics events. E.g. "myAnalyticsHandler".

Performance

AttributeTypeDefaultDescription
cache-ttl number 300000 Cache TTL in milliseconds for product and config data. Default: 5 minutes.
mode string "online" Set to "offline" to enable offline-first mode (see Offline Mode).

Theme Customization

The widget supports three levels of customization: attributes, CSS variables, and a custom CSS file.

Attribute-based theming

<salesbooth-deal api-key="sb_pub_your_key" products="prod_a1b2c3d4" theme-color="#7c3aed" dark-mode="true" font-family="'Inter', sans-serif" border-radius="12px" logo-url="https://your-site.com/logo.png" button-style="outlined" ></salesbooth-deal>

Custom CSS (advanced)

For full control, link a custom CSS file. The widget uses shadow DOM, so your styles must be injected via custom-css:

custom-styles.css
/* Override widget CSS custom properties */ :host { --sb-primary: #7c3aed; --sb-primary-hover: #6d28d9; --sb-radius: 12px; --sb-font: 'Inter', sans-serif; } /* Override specific components */ .sb-btn-primary { text-transform: uppercase; letter-spacing: 0.05em; font-weight: 700; } .sb-header { border-bottom: 2px solid var(--sb-primary); }
<salesbooth-deal api-key="sb_pub_your_key" products="prod_a1b2c3d4" custom-css="https://your-site.com/custom-styles.css" ></salesbooth-deal>
CSS file must be on an accessible CORS-enabled URL

The custom-css URL is fetched by the browser. Ensure CORS headers allow requests from your page's origin, or host it on a CDN.

Dark mode

<!-- Always dark --> <salesbooth-deal dark-mode="true" ... /> <!-- Follow system preference (prefers-color-scheme) --> <salesbooth-deal dark-mode="auto" ... /> <!-- Toggle dynamically with JavaScript --> <script> const widget = document.querySelector('salesbooth-deal'); widget.setAttribute('dark-mode', window.matchMedia('(prefers-color-scheme: dark)').matches ? 'true' : 'false'); </script>

Event Handling

The widget emits DOM CustomEvents. Listen via addEventListener or the sb.on() SDK method.

Widget DOM events

const widget = document.querySelector('salesbooth-deal'); widget.addEventListener('salesbooth:deal-created', (e) => { const { deal_id, customer, total, currency } = e.detail; // Redirect, track, provision access... }); widget.addEventListener('salesbooth:deal-signed', (e) => { console.log('Contract signed for deal', e.detail.deal_id); }); widget.addEventListener('step.payment', (e) => { analytics.track('checkout_payment_step', { value: e.detail.total }); });

All widget events

Eventdetail payloadWhen fired
salesbooth:deal-created { deal_id, customer, total, currency, products } Deal submitted and confirmed by server
salesbooth:deal-signed { deal_id, signed_at } Contract signature captured
step.change { from, to, step_name } Customer navigates to a different step
step.products { products } Product selection step rendered
step.configure { product_id, options } Configuration step rendered
step.customer {} Customer info step rendered
step.payment { total, currency } Payment step rendered
step.confirmation { deal_id } Confirmation screen shown
widget.ready { version } Widget initialized and config loaded
widget.error { code, message } Unrecoverable widget error
offline.queued { queue_length } Deal queued while offline
offline.synced { synced_count } Offline queue flushed on reconnect

SDK event API (sb.on())

If you load the SDK separately from the widget, use the event emitter API:

// After loading salesbooth.js const sb = Salesbooth.init({ apiKey: 'sb_pub_your_key' }); // Subscribe to an event sb.on('deal.created', (data) => { console.log('Deal created:', data); }); // Subscribe to all events (wildcard) sb.on('*', (eventName, data) => { analytics.track(eventName, data); }); // Unsubscribe a specific handler const handler = (data) => { ... }; sb.on('deal.created', handler); sb.off('deal.created', handler); // Unsubscribe all handlers for an event sb.off('deal.created'); // Rate limit events sb.on('rate-limit-warning', ({ remaining, limit, reset }) => { console.warn(`API quota low: ${remaining}/${limit} remaining`); }); sb.on('rate-limited', ({ retryAfter }) => { showBanner(`Rate limited. Retrying in ${retryAfter}s...`); });

Offline Mode

The widget supports offline-first operation via a persistent IndexedDB queue. Deals created while offline are stored and automatically synced when connectivity is restored.

Enable offline mode

<salesbooth-deal api-key="sb_pub_your_key" products="prod_a1b2c3d4" mode="offline" ></salesbooth-deal>

How the offline queue works

// The widget automatically: // 1. Detects network loss via navigator.onLine + fetch failures // 2. Persists API requests to IndexedDB (database: "salesbooth_offline") // 3. Shows a queue badge: "2 deals pending sync" // 4. Replays the queue on reconnect using Idempotency-Key headers // 5. Removes successfully synced requests; keeps 5xx failures for retry // Listen for offline events const widget = document.querySelector('salesbooth-deal'); widget.addEventListener('offline.queued', (e) => { showBanner(`Deal saved offline. ${e.detail.queue_length} pending sync.`); }); widget.addEventListener('offline.synced', (e) => { showSuccess(`${e.detail.synced_count} deal(s) synced!`); });
Idempotency guarantees

All queued requests include an Idempotency-Key header. If a request is retried after a network failure, the server deduplicates it — you'll never create a duplicate deal.

Storage quota

// The widget monitors IndexedDB storage usage. // When usage exceeds 85% of quota, a warning is emitted: widget.addEventListener('widget.error', (e) => { if (e.detail.code === 'STORAGE_QUOTA_WARNING') { console.warn('Offline storage near limit:', e.detail.message); } });

ETag caching

The SDK uses HTTP ETags to avoid redundant data fetches. Product catalog and configuration responses are cached in memory and revalidated with If-None-Match headers.

// Control cache TTL via attribute <salesbooth-deal cache-ttl="600000" ... /> <!-- 10 minutes --> // Or disable caching entirely <salesbooth-deal cache-ttl="0" ... />

Mobile Responsiveness

The widget is fully responsive and touch-optimized by default. No additional configuration is required.

  • Fluid layout that adapts to any container width
  • Touch-friendly tap targets (minimum 44×44px)
  • Native mobile keyboard types on input fields
  • Swipe gestures for step navigation
  • Apple Pay / Google Pay on supported devices

Recommended container sizing

/* Let the widget fill its container */ salesbooth-deal { display: block; width: 100%; max-width: 480px; /* recommended max for single-column layout */ margin: 0 auto; } /* For modal/popup embed */ .checkout-modal salesbooth-deal { width: 100vw; max-width: 560px; height: 100%; overflow-y: auto; }
Test on real devices

Use your browser's DevTools device emulation for initial testing, but always verify on physical iOS and Android devices before launch — especially the payment step.

ETag Caching & Performance

The widget uses HTTP ETags for efficient cache revalidation, reducing bandwidth and improving load times for returning customers.

How ETag caching works
// First load: full response GET /api/v1/widget-config?id=wc_abc123 Authorization: Bearer sb_pub_xxx < ETag: "abc123" < Cache-Control: max-age=300 // Subsequent loads: conditional request GET /api/v1/widget-config?id=wc_abc123 Authorization: Bearer sb_pub_xxx > If-None-Match: "abc123" < 304 Not Modified ← no body transferred, widget uses cached data

The widget stores ETags in memory (scoped per API key) and reuses them across step transitions. The cache is reset on page reload.

Retry behavior

The SDK automatically retries failed requests with exponential backoff:

// Default retry schedule: // Attempt 1: immediately // Attempt 2: 1 second delay // Attempt 3: 2 second delay // Attempt 4: 4 second delay (max 3 retries by default) // Listen for retry events sb.on('retry', ({ attempt, retryAfter, status }) => { console.log(`Retrying... attempt ${attempt}, wait ${retryAfter}s (HTTP ${status})`); });

Next Steps