App Bridge
App Bridge is the postMessage-based SDK that brokers communication between your app’s iframe and the LaunchMyStore host page — whether that host is the admin (the admin), the checkout, or the post-purchase / order-status page. It speaks one wire protocol everywhere; what changes is which actions the host happens to wire up. The SDK package is@launchmystore/app-bridge (vanilla TS) with a thin
React adapter at @launchmystore/app-bridge-react.
Installation
The SDK ships as CJS + ESM only (
dist/index.js and dist/index.mjs).
There is no public CDN build — if you need an inline build, build the
SDK locally and serve the bundle yourself.Quick Start
Core API
TheApp instance returned by createApp() exposes four methods:
| Method | Returns | Use for |
|---|---|---|
dispatch(action, payload?) | string (message id) | Fire-and-forget actions like TOAST_SHOW, REDIRECT, MODAL_CLOSE. |
dispatchAndWait(action, payload?) | Promise<payload> | Round-trip actions: SESSION_TOKEN_REQUEST, CART_GET, CLIPBOARD_READ_REQUEST. Rejects after 10s. |
subscribe(action, cb) | () => void (unsubscribe) | Listen for host-initiated actions like RESOURCE_PICKER_SELECTION, MODAL_PRIMARY_ACTION. |
getSessionToken() | Promise<string> | Shortcut for dispatchAndWait('SESSION_TOKEN_REQUEST') with caching. |
dispatch()
dispatchAndWait()
- Resolves with the host’s response payload (the bare payload — not
wrapped in another
payloadkey). - Rejects after 10 seconds with
Error('App Bridge: timeout waiting for <action> response'). - Rejects if the host returns an
errorstring withError('App Bridge: <action> failed: <error>').
subscribe()
getSessionToken()
getSessionToken() before every authenticated request and the SDK takes
care of caching. See Sessions & Authentication.
Action Surface
Action names are screaming-snake-case verbs, not object types. The SDK exports helper classes for the actions below in@launchmystore/app-bridge/actions — but whether an action does anything
on a given page depends on what that host has wired. The per-host
pages linked from the Hosts table are the authoritative source
of truth.
SDK-exposed action families
| Family | Actions | Hosts that wire it |
|---|---|---|
| Toast | TOAST_SHOW, TOAST_DISMISS | Admin, Checkout, Post-purchase |
| Modal | MODAL_OPEN, MODAL_CLOSE, MODAL_PRIMARY_ACTION, MODAL_SECONDARY_ACTION | Admin |
| ResourcePicker | RESOURCE_PICKER_OPEN, RESOURCE_PICKER_CLOSE, RESOURCE_PICKER_SELECTION, RESOURCE_PICKER_CANCEL | Admin |
| TitleBar | TITLE_BAR_UPDATE, TITLE_BAR_PRIMARY_ACTION, TITLE_BAR_SECONDARY_ACTION | Admin |
| NavigationMenu | NAVIGATION_MENU_UPDATE, NAVIGATION_MENU_NAVIGATE | Admin |
| ContextualSaveBar | CONTEXTUAL_SAVE_BAR_SHOW, CONTEXTUAL_SAVE_BAR_HIDE, CONTEXTUAL_SAVE_BAR_SAVE, CONTEXTUAL_SAVE_BAR_DISCARD | Admin |
| Loading | LOADING_START, LOADING_STOP | Admin |
| LeaveConfirmation | LEAVE_CONFIRMATION_ENABLE, LEAVE_CONFIRMATION_DISABLE | Admin |
| SessionToken | SESSION_TOKEN_REQUEST | Admin, Checkout, Post-purchase |
| Clipboard | CLIPBOARD_WRITE, CLIPBOARD_READ_REQUEST | Admin |
| History | HISTORY_PUSH, HISTORY_REPLACE, HISTORY_GO, HISTORY_BACK, HISTORY_FORWARD | Admin |
| Redirect | REDIRECT | Admin, Checkout, Post-purchase |
| Lifecycle | LIFECYCLE_FOCUS, LIFECYCLE_BLUR, LIFECYCLE_VISIBLE, LIFECYCLE_HIDDEN | Admin |
| Bridge | BRIDGE_PING | All hosts |
| Cart / Buyer-journey (checkout-only) | CART_GET, CART_LINES_CHANGE, DISCOUNT_CODE_CHANGE, GIFT_CARD_CHANGE, NOTE_CHANGE, ATTRIBUTE_CHANGE, CHECKOUT_TOTALS_GET, CUSTOMER_GET, CURRENCY_GET, ORDER_NOTE_SET, COUPON_APPLY_REQUEST, BUYER_JOURNEY_INTERCEPT, BUYER_JOURNEY_PROCEED | Checkout |
| Post-purchase (subset) | CART_LINES_CHANGE (add only), ORDER_GET, CUSTOMER_GET, CURRENCY_GET, REDIRECT, DONE | Post-purchase |
Other SDK action classes —
User, Print, Share, Scanner,
Fullscreen, Config, Environment, Features — exist in the SDK and
will dispatch postMessages, but no host currently handles them. They
will resolve via dispatch() (fire-and-forget) but a dispatchAndWait()
call will reject with a 10-second timeout. They are reserved for future
host implementations.- Actions Reference — payload shapes per action.
- App Bridge for Checkout — cart / discount / note / attribute mutators and the buyer-journey intercept.
Wire Format
The protocol is small enough to implement without the SDK if you need to. The iframe posts:Resize clamps depend on the host. Admin blocks: default 200, max 2000.
Admin Action modals: default 400, max 800 (and no
extensionId is
echoed — the modal already knows which extension it loaded). Checkout
extension slots: default 60, max 2000.React Integration
The React adapter wraps the same SDK in a context provider plus a hook for every action family.useAppBridge, useToast,
useModal, useConfirmationModal, useResourcePicker,
useProductPicker, useCollectionPicker, useCustomerPicker,
useFilePicker, useContextualSaveBar, useDirtyState, useTitleBar,
useNavigationMenu, useSessionToken, useAuthenticatedFetch,
useAppQuery, useAppMutation, useRedirect, useLoading,
useAppSubscription, useAppDispatch, useAppDispatchAndWait,
useUser, useConfig, useEnvironment, useFeatures, useFullscreen,
useLeaveConfirmation, useUnsavedChanges, usePrint, useShare,
useClipboard, useCopyToClipboard, useHistory, useLifecycle
(useOnFocus, useOnBlur, useOnVisible, useOnHidden), useCart.
Full list with signatures: React Hooks Reference.
Resource Picker
The picker action handles the eleven entity types your app might want to let merchants choose from.Supported resourceType values
| Type | PascalCase | lowercase alias |
|---|---|---|
| Product | Product | product |
| Product variant | ProductVariant | product_variant |
| Collection | Collection | collection |
| Customer | Customer | customer |
| Order | Order | order |
| Page | Page | page |
| Blog | Blog | blog |
| Article | Article | article |
| File / media | File | file |
| Metaobject | Metaobject | metaobject |
| Navigation menu | Menu | menu |
Session Tokens
GET /api/apps/session-token proxied through the admin. Verify tokens on
your backend with jsonwebtoken.verify(token, clientSecret, { audience, issuer: 'https://launchmystore.io' }) — the issuer is the full URL, and
the per-token id claim is sid (not jti). See Sessions &
Authentication for the full claim set and the
verification recipe.
Hosts
App Bridge is the same SDK regardless of where your iframe is mounted — but the wired action set differs per host:| Host | Wired actions | Reference |
|---|---|---|
| Admin | Modal, ResourcePicker, TitleBar, NavigationMenu, ContextualSaveBar, Loading, LeaveConfirmation, Toast, Redirect, Clipboard, History, Lifecycle, SessionToken | Admin extensions |
| Checkout | Cart / Discount / GiftCard / Note / Attribute / BuyerJourney + Toast + SessionToken | Checkout host |
| Post-purchase | Toast + Redirect + ORDER_GET + CUSTOMER_GET + CURRENCY_GET + CART_LINES_CHANGE (add only) + DONE + SessionToken | Post-purchase extensions |
RESOURCE_PICKER_OPEN,
the promise rejects ten seconds later with the timeout message.
Identifying the shop and user
The host pushes anEXTENSION_CONTEXT payload to every iframe on mount
(triggered by APP_BRIDGE_READY, and on demand via
EXTENSION_CONTEXT_GET). In React, read it via useApi().data; in vanilla
JS, subscribe to the EXTENSION_CONTEXT action or read
window.__LMS_EXTENSION_CONTEXT__ once the SDK has populated it.
The host sources shop and user from your /accounts row and is
populated for every authenticated admin session.
Tenant + auth (always present)
| Field | Source / Default | What it identifies |
|---|---|---|
domainSlug | domainslug cookie | Tenant key required by every REST call (?domainSlug=). |
adminToken | merchant JWT | Forwarded merchant JWT for host-side admin endpoints. Prefer App Bridge session tokens for your own API. |
shop (mirrors app.shop)
| Field | Notes |
|---|---|
shop.storeId | Immutable store UUID — key app data on this, never on domainSlug. |
shop.shopDomain | Full <slug>.launchmystore.io host. |
shop.primaryDomain | Custom domain when connected; null otherwise. |
shop.hasCustomDomain | true if the merchant has a published custom domain. |
shop.shopName | Store / business name. Falls back to domainSlug. |
shop.country | Defaults to "United States". |
shop.currency / currencySymbol | Derived from country by the host. |
shop.locale | Defaults to "en". |
shop.timezone | Reserved — defaults to "UTC"; store-level timezone is not yet a settable field. |
shop.plan.type | "Trial" | "Starter" | "Gold" | "Platinum". |
shop.plan.status | "active" | "expired" | "canceled". |
shop.plan.expiresAt | ISO date. |
user (mirrors app.staffMember)
| Field | Notes |
|---|---|
user.id | The merchant or staff user id. |
user.email | Account email. |
user.name | Owner / staff name. |
user.role | "merchant" | "staff" | "manager" | "staff_admin". |
user.locale | Defaults to "en". |
user.timezone | Defaults to "UTC". |
app + slot context
| Field | Type | What it identifies |
|---|---|---|
app.handle | string | Your app’s slug (matches app.json handle). |
app.id | string | Your app’s id. |
app.apiKey | string | Your app’s client id (safe to expose to the iframe). |
target | string | Extension placement (e.g. product.details.block, app.home). |
resourceId / productId / orderId / customerId / … | string | The resource the merchant is looking at. |
extensionId | string | Manifest id — echo it back in APP_BRIDGE_RESIZE. |
domainSlug + shop.storeId are the two fields you should persist data
against. domainSlug can change if a merchant re-keys their store;
shop.storeId is immutable.Security
Origin checked on both sides
Origin checked on both sides
The SDK only accepts messages from
atob(config.host). The host only
accepts messages whose payload.apiKey matches an installed app. A
rogue iframe in a different origin cannot forge a message that the
host will act on.Never embed your client secret
Never embed your client secret
Session tokens are signed on the host’s server using the client
secret stored against your app row. The iframe only ever sees the
signed JWT — never the secret.
Iframes are sandboxed
Iframes are sandboxed
Hosts mount iframes with
sandbox="allow-scripts allow-forms allow-popups allow-same-origin".
No access to the parent DOM, no access to the parent’s cookies — only
the postMessage channel.Validate user input server-side
Validate user input server-side
Anything posted from the iframe is in the user’s browser. Always
re-validate on your backend before persisting.
See Also
- Actions Reference — exhaustive payload shapes.
- App Bridge for Checkout — cart, discount, note, attribute mutations.
- App Bridge for Admin — modal/picker/title-bar wiring.
- React Hooks — hook-by-hook reference.
- Sessions & Authentication — JWT verification.