Oyster Widget Developer Docs (v1)
v1 supports two modes: float (launcher button) and inline (embedded into a container you provide). Use the playground to generate copyable integration code for your exact settings.
Inline is embedded
Inline mode mounts the widget into your page via container. No floating toggle button is used in inline mode.
autoOpen
autoOpen controls whether the widget surfaces on init. In float mode the launcher is always shown — autoOpen: false just keeps the widget closed until the user clicks the launcher (or you call open()). In inline mode, false defers mounting entirely.
User is optional
If you don’t have user context (public page), omit user. The widget can prompt for an email when needed.
Overview
The Oyster widget is embedded by loading a UMD bundle that exposes a global OysterskinWidget. You create a widget instance using one of the factory methods:
- OysterskinWidget.createScanWidget(options)
- OysterskinWidget.createChatWidget(options)
- OysterskinWidget.createUnifiedWidget(options)
- OysterskinWidget.createConsultationWidget(options)
- OysterskinWidget.createVoiceAgentWidget(options)
Each factory returns an instance with open(), minimize(), and close(). The unified widget instance additionally exposes openScan(), openConsultation(), openChat(), and openVoiceAgent() for driving the widget to a specific view from host code.
In v1, minimize() only affects float mode (it returns the widget to the launcher). In inline mode, minimize() is a no-op.
Quickstart
Minimal standalone examples: float mode and inline mode.
Playground
This snippet reflects your current playground settings. Your public key is never inlined; replace YOUR_PUBLIC_KEY.
Loading the bundle
<script src="https://sandbox.widget-lib.oysterskin.com/v1/oysterskin-vendor-widget-web.umd.js"></script>
Modes (float vs inline)
Float mode
Create the widget with mode: "float". The floating launcher appears automatically on every embed. autoOpen: true (default) also opens the widget itself immediately; autoOpen: false leaves just the launcher, and the widget opens on click or when you call open().
Inline mode
Create the widget with mode: "inline" and provide a container. Inline mode mounts into your page and does not use a floating launcher.
Upgrade guide (v0.2.0 → v1)
v1 is the recommended integration target. If you’re upgrading from v0.2.0, these are the changes you must make.
1) Inline mode is now truly embedded
- v0.2.0: mode: "inline" injected a fullscreen, fixed-position widget when you called open().
- v1: mode: "inline" mounts into a container you provide via container.
- v1: inline mode does not use a floating launcher button (no toggle button).
// v0.2.0 (inline)
const instance = OysterskinWidget.createScanWidget({
publicKey: "YOUR_PUBLIC_KEY",
user: { id: "YOUR_END_USER_ID" },
mode: "inline",
callback: onWidgetMessage,
});
instance.open();
// v1 (inline) — add a mount container + pass container
//
const instanceV1 = OysterskinWidget.createScanWidget({
publicKey: "YOUR_PUBLIC_KEY",
// user is optional in v1
// user: { id: "YOUR_END_USER_ID" },
mode: "inline",
container: "#widget-root",
inlineHeight: 720,
callback: onWidgetMessage,
});
// autoOpen defaults to true in v1 (mounts immediately)
2) autoOpen controls whether the widget surfaces automatically
- v1: autoOpen defaults to true.
- v1 + float: the launcher is always shown. autoOpen only controls whether the widget itself opens on load — true: opens immediately; false: the widget stays closed until the user clicks the launcher (or you call open()).
- v1 + inline: if autoOpen: true, the widget mounts immediately into container. If false, it mounts when you call open().
- Changed from v1.9.x: previously, autoOpen: false in float mode rendered nothing — including the launcher. From v1.10.0 the launcher is always present in float mode; autoOpen only gates the widget itself.
// v1: delay rendering until a CTA click
const instance = OysterskinWidget.createUnifiedWidget({
publicKey: "YOUR_PUBLIC_KEY",
mode: "inline",
container: "#widget-root",
inlineHeight: 720,
autoOpen: false,
callback: onWidgetMessage,
});
// Later, on user intent:
instance.open();
3) User is optional in v1
- If your widget loads on public pages (no user context), omit user.
- If you do have a stable user id, pass it for better continuity.
Widget types
- Scan: createScanWidget
- Chat: createChatWidget
- Unified: createUnifiedWidget
- Consultation: createConsultationWidget
- Voice Agent: createVoiceAgentWidget
Unified widget: programmatic navigation
On a unified widget instance you can jump directly to any tile from host code. Each method opens the widget (if minimized) and navigates it to that view.
const widget = OysterskinWidget.createUnifiedWidget({ /* ... */ });
widget.openScan();
widget.openConsultation();
widget.openChat();
widget.openVoiceAgent();
These methods are present on every widget instance, but on a restricted widget (e.g. createScanWidget) the widget silently ignores navigation to disallowed views.
Unified widget: restricting the gallery
Pass widgetTypes to show only specific tiles in the home gallery. The router also blocks deep-linked routes outside the listed types.
const widget = OysterskinWidget.createUnifiedWidget({
publicKey: "YOUR_PUBLIC_KEY",
widgetTypes: ["scan", "consultation"], // hide chat + voice-agent tiles
callback: (message) => console.log(message),
});
Vendors can also configure this list centrally from the Oyster widget manager — no host code change required. When widgetTypes is passed from code it overrides the vendor-managed value.
Where config comes from
Most appearance and copy options can be set centrally from the Oyster Widget Manager in the vendor dashboard and are fetched at runtime. The recommended workflow:
- Set your branding, copy, and budget tiers once in the Widget Manager — no deploy required, every embed picks them up.
- From code, pass only what's page-specific or what you need to override for a particular widget instance. Anything you pass from code wins; anything you omit falls back to the manager-saved value.
Manager-controlled options
Set these in the Widget Manager; override per-instance from code only when needed.
- primaryColor, primaryTextColor
- displayLogo, logoUrl
- introMessage, messageBody
- budgetRanges, budgetCurrency, minBudget/maxBudget
- conversationStarters (chat / unified)
- widgetTypes (unified — restrict the home gallery)
- Allowed domains
Code-only options
No manager equivalent — these are per-page or per-instance decisions.
- publicKey, callback — required on every instance
- mode, container, inlineWidth, inlineHeight — page-specific layout
- autoOpen, displayHoverPopup, displayAutoPopup — per-instance UX behaviour
- user — per-end-user context
In short: configure once in the Widget Manager; reach for code overrides only when an instance needs to differ.
Options reference
- key (string, required): vendor key.
- callback (function, required): called with widget events.
- mode ("float" | "inline", optional): defaults to "float".
- autoOpen (boolean, optional): defaults to true. Auto-opens the widget on init. In float mode the launcher is always shown — autoOpen only controls whether the widget also surfaces immediately. Set to false to keep only the launcher visible until the user clicks it (or you call open()).
- user (object, optional): { id, name?, email? }. If omitted, the widget can prompt for email when needed.
- container (HTMLElement | string, inline only): where the widget mounts.
- inlineWidth/inlineHeight (number | string, inline only): number = px.
- primaryColor (string, optional): widget theme color. Defaults vary by widget type.
- displayLogo (boolean, optional): whether to show the Oysterskin logo. Defaults to true.
- logoUrl (string, optional): custom logo image URL. When provided with displayLogo: true, replaces the default SVG logo with your custom image.
- introMessage (string, optional): launcher tooltip title text.
- messageBody (string, optional): launcher tooltip body text.
- displayHoverPopup (boolean, optional, float only): defaults to true. Show the launcher tooltip when the user hovers the launcher.
- displayAutoPopup (boolean, optional, float only): defaults to true. Auto-show the launcher tooltip (with a close button) once per browsing session per publicKey, after the host page finishes loading. Suppressed when autoOpen: true or once the user has opened the widget at least once.
- minBudget/maxBudget (number, optional): fallback budget boundaries. Defaults to 100 and 100000.
- budgetCurrency (string, optional): preferred currency code used for budget range rendering.
- budgetRanges (object, optional): config-driven range presets. Supports { low, mid, premium } or { NGN: { low, mid, premium } }.
- conversationStarters (string[], chat/unified only): optional suggestion prompts.
- widgetTypes (("scan" | "chat" | "consultation" | "voice-agent")[], unified only): restrict which tiles appear in the home gallery. Omit (or pass empty) to show all. Vendor-managed value in the Oyster admin is used when this option is not provided.
Budget ranges example
const instance = OysterskinWidget.createScanWidget({
publicKey: "YOUR_PUBLIC_KEY",
budgetCurrency: "NGN",
budgetRanges: {
NGN: {
low: { min: 30000, max: 60000, label: "Low Budget" },
mid: { min: 60000, max: 100000, label: "Mid Budget" },
premium: { min: 100000, max: 200000, label: "Premium" }
}
},
callback: (message) => console.log(message),
});
Custom logo example
Use logoUrl to display your own logo instead of the default Oysterskin logo:
const instance = OysterskinWidget.createScanWidget({
publicKey: "YOUR_PUBLIC_KEY",
mode: "float",
displayLogo: true,
logoUrl: "https://your-domain.com/logo.png",
callback: (message) => console.log(message),
});
Events and callback
Your callback receives { event, data }. Events are emitted by the widget via postMessage, and forwarded by this library after origin/source checks.
Supported callback events
- closed: user closed the widget UI. data: {}
- minimized: user minimized the widget UI (float mode only). data: {}
- checkout: user clicked checkout from within the widget. data: CheckoutPayload
- cartItemAdded: an item was added or its quantity was increased. data: CartItemChangedPayload
- cartItemRemoved: an item was removed or its quantity was decreased. data: CartItemChangedPayload
- scanCompleted: scan completed and produced a batch id. data: { batch_id }
Recommended handler pattern
function onWidgetMessage(message) {
switch (message.event) {
case 'checkout':
// message.data: { total_quantity, total_amount, currency, checkout_items, user? }
// `user` is { id?, name?, firstName?, lastName?, email? } when the widget
// has a profile on hand (collected before scan or supplied via config.user),
// otherwise omitted entirely.
break;
case 'cartItemAdded':
case 'cartItemRemoved':
// message.data: { total_quantity, changed_item, checkout_items }
break;
case 'scanCompleted':
// message.data: { batch_id }
break;
case 'minimized':
case 'closed':
break;
default:
// Future-proofing: ignore unknown events
break;
}
}
Example payloads
checkout
{
"event": "checkout",
"data": {
"total_quantity": 2,
"total_amount": 12800,
"currency": "NGN",
"checkout_items": [
{
"product_id": 123,
"product_name": "Name",
"quantity": 2,
"brand_name": "Brand",
"sku": "SKU-123",
"image_url": "https://example.com/image.jpg",
"stock_level": 10
}
],
"user": {
"id": "user-123",
"firstName": "Jane",
"lastName": "Doe",
"email": "jane@example.com"
}
}
}
user is included when the widget has a profile on hand — either collected before the scan (contact step) or supplied via config.user. It is omitted entirely when nothing is known, so you can distinguish "no profile" from "anonymous". All fields inside user are optional.
cartItemAdded (payload shape is identical for cartItemRemoved)
Tip: checkout_items can be an empty array if the widget doesn’t yet have inventory-backed product IDs for the items in the cart.
{
"event": "cartItemAdded",
"data": {
"total_quantity": 1,
"changed_item": {
"product_id": 123,
"product_name": "Name",
"quantity": 1,
"brand_name": "Brand",
"sku": null,
"image_url": null,
"stock_level": null
},
"checkout_items": []
}
}
scanCompleted
{
"event": "scanCompleted",
"data": {
"batch_id": "batch_abc123"
}
}
Framework patterns
Recommended: load the UMD script once, keep one widget instance, and reuse it. In inline mode, mount into a stable container.
Advanced patterns
For inline mode, use autoOpen: false + call open() from your CTA to defer the widget entirely and improve page performance. (In float mode the launcher is always shown, so autoOpen: false just keeps the widget itself closed — it doesn't avoid loading the launcher.)
Versioning
In production, the recommended default is to pin to the latest major: /v1/. This lets you receive updates and patches without chasing every release.
// Production (major channel)
<script src="https://widget-lib.oysterskin.com/v1/oysterskin-vendor-widget-web.umd.js"></script>
// Sandbox (major channel)
<script src="https://sandbox.widget-lib.oysterskin.com/v1/oysterskin-vendor-widget-web.umd.js"></script>
If you need fully immutable builds (e.g. regulated environments), pin an exact version like /v1.0.0/ and upgrade intentionally.
CSP / security headers
Allow the widget script + widget origins for the environment you use.
Troubleshooting
If OysterskinWidget is undefined, verify your script URL and CSP.
Launch checklist
- Keys: no keys committed; loaded at runtime from your config/secret system.
- Callback: events are handled and logged in staging.
- CSP: script-src and frame-src updated for the widget domains.
- Inline container: ensure your container exists before creating the widget.