Before you write any code
- Register every embedding origin with Libra — development, staging and production — before testing. An unregistered origin does not report itself: the popup closes with
reason: 'popup_closed'and the widget then sits on the login screen forever. Authentication - Pin
baseUrlto an allowed origin. Only*.libratech.aiandlocalhostare accepted; anything else throws atinit(). API reference
Lifecycle
init()once at startup,attach()on user intent.init()is free (no network); the session starts on the firstattach(). Trigger it from a click so the auth popup is not blocked. Embedding- Never call
init()twice. The second call is a silent no-op and your new config is discarded.destroy()first if you must reconfigure. - Keep the attach target mounted. Hide the container with CSS rather than unmounting it, or the SDK detaches itself on the next frame. Panel and placement
- Never destroy to hide.
detach()keeps the conversation;destroy()throws it away. Reservedestroy()for teardown. - Decide header ownership up front. Either pass
onCloseand let the widget draw its Libra headbar, or omit it and render your own close control that callsLibra.detach(). Don’t do both. Header ownership
Layout
- Mount into an element you control with
mountTarget, give itisolation: isolate, and set a modestzIndex. Then your overlays stack above the widget with ordinary z-index values. Stacking context - Give the panel at least 380–400px. Below 400px the composer controls shrink; above it, extra width only reflows content — it never reveals more features. Panel and placement
- Decide header ownership deliberately. Without
onClosethe iframe has no close control at all, so your product must provide one. - Keep
transform,filter,perspective,backdrop-filter,will-changeandcontainoff the mount target’s ancestor chain. Any of them silently misplaces the fixed-position wrapper; the SDK warns in the console when it spots one. - Never reparent the mount target. Moving an iframe in the DOM reloads it and ends the session.
Locales
- Register only the locales you ship, and register them before
init(). Unregistered locales throw at the call site, by design. Locales - Load locale bundles in parallel with the main bundle (
Promise.allon the CDN, or imports in your bundler) so the extra language costs no wall-clock time.
Context and citations
- Set the suggestion when the document view mounts, clear it when it unmounts. A stale chip attaches the wrong document. Document suggestions
- Claim only your own citations. In
onCitationClick, checkevent.data.sourceand callpreventDefault()for sources you can render; leave the rest to the default new tab. Citation clicks - Use
contentto scroll. When present it is the exact cited passage; search for it in your viewer after navigating tourl. - Call
preventDefault()first, then navigate. If your handler throws afterwards the SDK still opens its default tab, but only if you had not already claimed the citation. - Don’t expect a document viewer. Uploaded-document citations have no “To source” control inside the widget. Citation clicks
Security and headers
- Allow
blob:frames and same-origin framing on every SDK-enabled page; neverX-Frame-Options: DENY. Security headers - Allow
microphone=(self)if you set aPermissions-Policy, or voice input silently fails.
Proxy mode
- Cache the token exchange per user (~60 s). The SDK bursts requests on load; a fresh exchange per call trips Auth0’s rate limit. Proxy mode
- Recover in the BFF, not in the browser. The widget only understands
401(show login) and “anything else” (generic error). Refresh expired product tokens and retry transient failures before a response reaches the SDK. - One correlation ID per request, and never a token in a log. Error monitoring on the BFF is required; it is the only place auth failures are visible.
- Forward
X-Libra-SDK-Versionas well asX-Libra-SDK-Product-ID. A header allowlist built from the SDK README drops the version header, and the backend then falls back to pre-0.10.1 behaviour that inlines chat images as base64. Proxy mode
Releases
- Pin the version in CDN URLs and in
package.json, and read the changelog before moving it. Breaking releases are tagged and have renamed or removed API in the past (research-source slugs,containerId,onExternalDocumentCitationClick). - Try it on the playground first. It embeds the current staging build and exercises every API call from a plain host page.

