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

What it is, how it behaves

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

Copy/paste baseline integration

Minimal standalone examples: float mode and inline mode.

Playground

Generate code from your settings
Important Your vendor key is never stored by this page (other non-secret fields may be remembered locally).
Prefer pinned URLs in production.
Chat and Unified can accept conversation starters.
Inline mounts into container. Float shows a launcher button.
If false, nothing renders until you call open().
This doc does not store it
If omitted, the widget can prompt for an email when needed.
Inline mode requires a mount container. This docs page includes a container with id inlineMount.
CSS selector or HTMLElement.
Number = px, or any CSS length.
Inline mount container
Custom logo image URL. When provided with displayLogo=true, replaces the default SVG logo.
Used by the float launcher hover popup text.
Optional suggestions shown in the widget.
Event log (callback payloads)

This snippet reflects your current playground settings. Your public key is never inlined; replace YOUR_PUBLIC_KEY.

Loading the bundle

UMD + global API
<script src="https://sandbox.widget-lib.oysterskin.com/v1/oysterskin-vendor-widget-web.umd.js"></script>

Modes (float vs inline)

Decide how the widget appears

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)

Breaking changes + what to change

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

Which factory method to call
  • Scan: createScanWidget
  • Chat: createChatWidget
  • Unified: createUnifiedWidget
  • Consultation: createConsultationWidget

Options reference

What you can configure
  • 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

How to listen and react

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 }
Note oysterSkinWidget.resize is an internal sizing message used to resize the inline iframe. It is not forwarded to your callback.

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

React/Next.js, SPAs, tag managers

Recommended: load the UMD script once, keep one widget instance, and reuse it. In inline mode, mount into a stable container.

Advanced patterns

Lazy load, singleton, CTA-driven open

Use autoOpen: false + call open() from your CTA to improve page performance.

Versioning

Pin versions and promote safely

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

Common integration blocker

Allow the widget script + iframe origins for the environment you use.

Performance

Load timing + UX

Inline + autoOpen:false is the best default for page performance.

Troubleshooting

Fast fixes

If OysterskinWidget is undefined, verify your script URL and CSP.

Launch checklist

Before you ship
  • 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.