# App Bridge SDK: build admin and storefront extensions

APP BRIDGE SDK: BUILD ADMIN AND STOREFRONT EXTENSIONS

App Bridge is the JavaScript SDK that connects your app's iframe to the
LaunchMyStore admin host. With one createApp call and a handful of dispatch
actions, you can show toasts, open modals, pick resources, set the title bar,
request session tokens, and drive 21 other host capabilities — all without ever
exposing the merchant's storefront origin to your iframe.


WHAT IS APP BRIDGE

Admin extensions run inside a sandboxed iframe in the merchant's admin. The host
page (the admin shell) and your iframe sit on different origins, so you can't
reach into each other directly. App Bridge wraps window.postMessage into a clean
request/response API:

 1. Your iframe calls app.dispatch('TOAST_SHOW', { message: 'Saved' }).
 2. The SDK posts a structured message to the host with a unique id.
 3. The host runs the action (renders the toast, opens the modal, queries the
    database, whatever).
 4. If the action returns data, the host posts a response back; the SDK resolves
    your promise.


INSTALL AND BOOTSTRAP

npm install @launchmystore/app-bridge @launchmystore/app-bridge-react

The host injects two values into your iframe URL: ?apiKey=...&host=... (where
host is a base64-encoded origin). Pass them to createApp:

import { createApp } from '@launchmystore/app-bridge';

const params = new URLSearchParams(window.location.search);
const app = createApp({
  apiKey: params.get('apiKey'),
  host: params.get('host'),
});

That's it — you now have a configured App instance and the SDK is listening for
responses from the host.


THE 21 ACTION FAMILIES

App Bridge groups actions into family modules. The full set:

 * Toast — TOAST_SHOW, TOAST_HIDE; success/error/info variants.
 * Modal — open a host-rendered modal with an iframe URL or inline title + body.
 * ResourcePicker — 11 picker types: collection, product, blog, article, page,
   menu, customer, order, product_variant, file, metaobject.
 * TitleBar — set page title + primary/secondary action buttons.
 * NavigationMenu — register secondary nav items under your app.
 * ContextualSaveBar — the "Unsaved changes" bar at the top of the admin.
 * Loading — toggle the top progress bar.
 * Fullscreen — expand your iframe to fill the viewport.
 * LeaveConfirmation — warn before navigating away with unsaved state.
 * SessionToken — app.getSessionToken() returns a short-lived JWT for API calls.
 * User — current staff identity (id, name, email, locale).
 * Config — store config (currency, country, plan).
 * Environment — runtime info (host version, theme mode).
 * Features — feature flags exposed by the host.
 * Print — trigger window.print on a host page.
 * Share — native share sheet via the host.
 * Clipboard — write/copy iframe-side; read/paste via the host (Chrome blocks
   clipboard read in cross-origin iframes).
 * Scanner — barcode/QR scan via host camera.
 * History — push/replace state in the admin URL bar.
 * Lifecycle — READY, BEFORE_UNLOAD notifications.
 * Redirect — app.redirect.dispatch({ url, newContext }) — navigate the parent.


DISPATCHING ACTIONS

Fire-and-forget:

app.dispatch('TOAST_SHOW', {
  message: 'Settings saved',
  duration: 3000,
  isError: false,
});

Request/response (waits for the host to reply):

const result = await app.dispatchAndWait('RESOURCE_PICKER_OPEN', {
  type: 'product',
  multiple: true,
});

console.log(result.selection);  // array of selected products


SESSION TOKENS FOR API CALLS

Your app's backend needs to know which merchant is calling it. App Bridge issues
short-lived JWTs signed with your client secret:

const token = await app.getSessionToken();

fetch('/api/save-settings', {
  method: 'POST',
  headers: { Authorization: `Bearer ${token}` },
  body: JSON.stringify({ ... }),
});

Tokens are cached and auto-refreshed by the SDK. Verify them server-side using
your app's client secret.


REACT BINDINGS

If you're building with React, install @launchmystore/app-bridge-react for hooks
and providers:

import { AppBridgeProvider, useAppBridge } from '@launchmystore/app-bridge-react';

function App() {
  return (
    <AppBridgeProvider apiKey={apiKey} host={host}>
      <SettingsPage />
    </AppBridgeProvider>
  );
}

function SettingsPage() {
  const app = useAppBridge();
  return <button onClick={() => app.dispatch('TOAST_SHOW', { message: 'Hi' })}>Toast</button>;
}


THE POSTMESSAGE WIRE FORMAT

If you're debugging or building a non-JS client, here's the contract the host
listens for:

// iframe → host
{ type: 'APP_BRIDGE_ACTION', action: 'TOAST_SHOW', id: 'ab_123', payload: { ... } }

// host → iframe (response)
{ type: 'APP_BRIDGE_RESPONSE', action: 'TOAST_SHOW', id: 'ab_123', payload: { ... } }

// iframe self-resize
{ type: 'APP_BRIDGE_RESIZE', extensionId: 'your-manifest-id', height: 480 }

The host silently drops messages that don't carry type: 'APP_BRIDGE_ACTION'. For
resize, the extensionId must match the manifest id the host passes via
?extensionId= — otherwise your iframe stays at the 200px default and looks
empty.


FAQ


DO I HAVE TO USE APP BRIDGE FOR ADMIN EXTENSIONS?

Yes, in practice. The host enforces cross-origin isolation, so direct DOM access
is impossible. App Bridge is the only sanctioned way to interact with the admin
chrome (toasts, modals, navigation).


WHERE DO ADMIN EXTENSIONS SHOW UP IN THE ADMIN?

There are 26 verified injection points including product details, order details,
customer details, collection details, discount details, gift card list, blog
list, contact list, analytics, inventory, shipping settings, payment settings,
POS settings, account settings, order create, and more. Each extension's
manifest declares its target.


DOES APP BRIDGE WORK IN CHECKOUT AND POST-PURCHASE EXTENSIONS?

Yes. Pass host: 'checkout' or host: 'post-purchase' as a sentinel value — the
SDK detects these and treats the iframe as same-origin instead of decoding
base64.


HOW DO I OPEN A MODAL FROM INSIDE MY APP?

app.dispatch('MODAL_OPEN', { url: '/extensions/edit', title: 'Edit', size:
'large' }). The host renders the modal chrome and loads your URL in a nested
iframe. Use MODAL_CLOSE to dismiss programmatically.


WHY IS MY IFRAME STUCK AT 200PX TALL?

Almost always a mismatched extensionId on the resize message. Read the
extensionId from your iframe's URL query and echo it back exactly in the
APP_BRIDGE_RESIZE payload.


HOW LONG DOES A SESSION TOKEN LAST?

The JWT carries a short expiry (typically a minute). The SDK caches it and
refreshes automatically before it expires, so application code can call
getSessionToken() as often as it likes without worrying about lifecycle.