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 iframe into your page via container. No floating toggle button is used in inline mode.
autoOpen
autoOpen controls whether anything renders automatically. If false, you call open() when you’re ready.
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)
Each factory returns an instance with open(), minimize(), and close().
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". If autoOpen: true, the floating launcher appears immediately.
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 iframe 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 anything renders automatically
- v1: autoOpen defaults to true.
- v1 + float: if autoOpen: true, the launcher appears immediately. If false, nothing renders until you call open().
- v1 + inline: if autoOpen: true, it mounts immediately into container. If false, it mounts when you call open().
// 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
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.
- 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 popup title text.
- messageBody (string, optional): launcher popup body text.
- minBudget/maxBudget (number, optional): budget range for product recommendations. Defaults to 100 and 100000.
- conversationStarters (string[], chat/unified only): optional suggestion prompts.
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 core 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 }
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
}
]
}
}
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
Use autoOpen: false + call open() from your CTA to improve page performance.
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 + iframe origins for the environment you use.
Performance
Inline + autoOpen:false is the best default for page performance.
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.