Lumi SDK script tag

One async <script> tag in your <head>, one or more <div data-lumi-slot> elements where you want ads. No backend changes. Works with any LLM provider (OpenAI, Anthropic, Gemini, open-source) and any framework (LangChain, Vercel AI SDK, CrewAI, plain React) — Lumi attaches at the rendering surface.

v0.1 — beta. Live on boostboss.ai/lumi.js today.

The slot-based API documented on this page is shipped. <script src="https://boostboss.ai/lumi.js"> resolves and works against the live Boost Boss backend. Try the sandbox flow with data-publisher-id="pub_test_demo" — no signup required to verify integration end-to-end. The legacy BoostBoss.init API at boostboss.ai/sdk.js remains supported for backward compatibility. Demand-side fill is in early beta; expect lower fill rates than mature ad networks until our Founding Publisher cohort fills out.

Prerequisites

Installation

Add the snippet to your <head>. Async; no impact on first paint.

<script async
  src="https://boostboss.ai/lumi.js"
  data-publisher-id="pub_xxx">
</script>

Replace pub_xxx with your publisher ID from the dashboard.

Slot placement

Drop a <div> with data-lumi-slot wherever you want an ad. Lumi auto-discovers slots on page load and on DOM mutations (so SPA route changes work without ceremony).

<div data-lumi-slot="banner"></div>
<div data-lumi-slot="sidebar"></div>
<div data-lumi-slot="inline" data-lumi-context="checkout flow"></div>

Slot data attributes

AttributeValuesDescription
data-lumi-slotbanner · sidebar · inline · interstitialAd format. Lumi picks creative dimensions accordingly.
data-lumi-contextany stringContext signal for matching. Defaults to page <h1> + URL path.
data-lumi-frequencyonce · session · alwaysHow often this slot refreshes. Default: session.
data-lumi-fallbackany HTML idElement to show when no ad fills the slot (e.g. your own promo).

Theming

Lumi reads CSS variables from the slot's computed style — set them once on :root or scope per slot.

:root {
  --lumi-primary:    #FF2D78;        /* CTA button color */
  --lumi-text:       #0F0F1A;        /* body text */
  --lumi-muted:      #6B7280;        /* sponsored label */
  --lumi-bg:         #FFFFFF;        /* card background */
  --lumi-radius:     12px;           /* corner radius */
  --lumi-font:       'Inter', sans-serif;
}

The default look is neutral; styled themes match your brand without a CSS reset war.

Programmatic API

For SPA route changes or programmatic refreshes, use the global window.Lumi object. Available after the script loads (lumi:ready event fires).

Lumi.refresh(slotId?)

Re-fetches and re-renders ads. Pass a slot element or selector to refresh just one slot; omit to refresh all.

// refresh all slots after a route change
router.on('navigated', () => Lumi.refresh());

// refresh a specific slot
Lumi.refresh('#main-banner');

Lumi.destroy()

Removes all rendered ads and disconnects observers. Call when you tear down a page section that contained Lumi slots.

Lumi.render(slotEl, opts)

Manually mount a slot. Useful for slots created after page load that aren't auto-discovered (rare; the MutationObserver covers most cases).

const el = document.getElementById('late-bound-slot');
Lumi.render(el, { format: 'inline', context: 'pricing page' });

Conversion tracking

For conventional advertiser-side conversions (the user clicks an ad, lands on a separate thank-you page, fires the conversion there) advertisers should install pixel.js on their landing/thank-you page — see the conversion beacon docs.

For publisher-side conversions — when the user converts inside the same surface that hosted the ad (in-app signup, in-page checkout) — call Lumi.trackConversion():

// After the user signs up / completes the action your ad promoted:
Lumi.trackConversion({
  type:       'signup',           // matches campaigns.conversion_event_types
  value:      29.99,              // USD; required for ROAS / target_roas optimization
  currency:   'USD',             // optional; defaults to 'USD'
  externalId: 'order_98123',     // optional — your order/user id for reconciliation
});

Lumi auto-resolves the adId + auctionId from the most recent slot it rendered. To target a specific slot when multiple ads are on screen, pass slot:

Lumi.trackConversion({ type: 'purchase', slot: '#sidebar-ad', value: 49.0 });

Failures emit a lumi:error event and are surfaced via the silent-failure observability endpoint.

Event handling

Lumi dispatches DOM events on window. Listen for any of:

window.addEventListener('lumi:ready', () => { /* SDK loaded */ });

window.addEventListener('lumi:impression', (e) => {
  const { adId, advertiserId, slot, format } = e.detail;
  analytics.track('ad_impression', { adId, format });
});

window.addEventListener('lumi:click',    (e) => { /* user clicked */ });
window.addEventListener('lumi:close',    (e) => { /* dismissed */ });
window.addEventListener('lumi:no_fill',  (e) => { /* slot stayed empty */ });
window.addEventListener('lumi:error',    (e) => { /* see e.detail.code */ });

SSR and SPA support

Lumi is fully client-side — render the slot DOM server-side, the SDK hydrates on load. Specifics for popular frameworks:

Next.js (App Router)

Add the script in app/layout.tsx via next/script with strategy="afterInteractive". Slots can live in any server or client component.

import Script from 'next/script';
export default function RootLayout({ children }) {
  return (
    <html><body>{children}
      <Script src="https://boostboss.ai/lumi.js"
        data-publisher-id="pub_xxx" strategy="afterInteractive" />
    </body></html>
  );
}

Nuxt 3

Use useHead in app.vue or a layout, with defer: true.

Remix

Add to root.tsx via <Scripts> sibling. No SSR-time fetch needed — Lumi only runs in the browser.

SPA route changes

Lumi's MutationObserver auto-detects slots that mount/unmount as you change routes. If you find specific slots aren't refreshing, call Lumi.refresh() explicitly in your route hook.

Error handling

The snippet never throws into your app. Failures resolve to a lumi:error event and the slot stays empty (or shows your data-lumi-fallback element if configured).

Error codeMeaning
BBX_AUTHPublisher ID unknown or revoked. Check dashboard.
BBX_CSPContent Security Policy blocks the script. See "CSP" below.
BBX_NETWORKNetwork failure or CORS issue. Slot stays empty.
BBX_NO_FILLNo matching advertiser. Not an error per se — emitted as lumi:no_fill for clarity.

CSP (Content Security Policy)

If your site uses CSP, allow Lumi's host:

script-src  'self' https://boostboss.ai;
connect-src 'self' https://boostboss.ai;
img-src     'self' https://boostboss.ai data:;

Testing

Use data-publisher-id="pub_test_demo" in development. Sandbox always returns a fixed creative regardless of context, so you can verify slot rendering in dev without hitting real demand-side calls.

<script async
  src="https://boostboss.ai/lumi.js"
  data-publisher-id="pub_test_demo"
  data-debug="true">
</script>

Set data-debug="true" to log every event to the console. Switch to your live publisher ID before deploying.

Going live

Troubleshooting

The slot is empty.

Check the console for lumi:no_fill or lumi:error events. no_fill means there's no matched advertiser right now; error means something's misconfigured (auth, CSP, network).

The script blocks page load.

It shouldn't — the snippet uses async. If you see a blocking warning in Lighthouse, double-check the async attribute is actually present on your <script> tag.

Ads don't refresh between SPA routes.

Most apps work without intervention thanks to MutationObserver. If yours doesn't, call Lumi.refresh() in your route-change hook (Next.js: useRouter().events; Vue Router: router.afterEach; React Router: useLocation with an effect).

Theming doesn't apply.

Make sure the CSS variables are set on a parent of the slot (most commonly :root). The slot reads getComputedStyle at mount time.

Ads pass page-speed budgets in dev but not production.

Caching: lumi.js is cached aggressively. Bust it by appending ?v=YYYYMMDD if you suspect stale SDK behavior. Most production issues are CDN-caching mismatches, not SDK regressions.

Changelog

See the API reference changelog for SDK release notes.