How the embedded UI works
When a merchant opens your app from the dashboard dock, your app’sappUrl is loaded in a
sandboxed <iframe> inside the dashboard. The dashboard appends a few context parameters so
your app can recognise the active store and user without a separate auth step:
These parameters are for convenience — they identify the context, not prove identity.
The actual authentication proof is your signed, HttpOnly session cookie set during the OAuth
install (see Install flow). Never trust
shop/user from the URL
without validating against your session.Background pre-loading
The dashboard pre-loads every installed app in a hidden iframe when the merchant opens the dashboard — before the merchant ever taps the dock icon. This means:- Your app’s code, assets, and connections are already warm by the time the merchant opens it.
- Background work (polling, WebSocket connections, sync) can start immediately, not only when the merchant looks at the app.
- The first open is instant — no loading spinner if your app has initialised in the background.
salesive:app-wake
postMessage. Register a handler for this to start background work:
fetch() calls all work normally in the background.
Full lifecycle
Every embedded app goes through these events:Session boundaries
Two further events fire when the ground shifts underneath your app — the merchant is no longer the same person, or is no longer looking at the same store:Runtime permissions
Some capabilities — expanding the app programmatically, reading the clipboard, cancelling orders — require explicit, time-limited merchant approval. Unlike OAuth install scopes (which are approved once at install), runtime permissions can be requested at any time via a dashboard modal the merchant approves or denies. The merchant chooses a duration (once, 1 day, 7 days, etc.) and can revoke at any time.Available runtime permissions
File picker
Your app can open the merchant’s Salesive media library and receive the files they pick — without building any upload UI or file storage of your own. CallpickFiles() and
the dashboard shows the same media manager used across Salesive: the merchant selects
existing files (or uploads new ones inline), and your app receives their public URLs.
The file picker needs no runtime permission — the merchant explicitly chooses each
file in the modal, the same consent model as a native file input. The picker only opens
while your app is in the foreground; a call from a backgrounded app resolves to an
empty array. See the
pickFiles() reference below for the full API.The App Bridge package
Install the helper:@types/ package needed.
onAppWake(callback)
Fires when the dashboard first loads the app in the background. This is the earliest point
at which your app’s JavaScript runs. Use it to start long-running background work.
onAppOpened(callback)
Fires each time the merchant expands the app to full view (first open or restore from
minimized). Use it to refresh UI data that may be stale after time in the background.
onAppMinimized(callback)
Fires when the merchant presses − (minimize) or swipes the sheet down on mobile. The app
is hidden but still running.
onAppClosed(callback)
Fires when the merchant presses × (close / dismiss). The app is still running in the
background — this is the same as minimized from a lifecycle standpoint, but signals that
the merchant deliberately dismissed the panel rather than just hiding it.
onLogout(callback)
Fires when the merchant logs out of Salesive.
The dashboard clears only its own storage on logout. Your app runs on a different
origin, so its cookies, localStorage, and tokens survive untouched — the next person to
log in on that browser would silently inherit the previous merchant’s app session. This
event is your only opportunity to prevent that.
Your app also receives the merchant’s
user id as a query param on every embed. Checking
it on boot is a useful belt-and-braces companion to this event: if user differs from the
one your stored session belongs to, force re-authentication regardless of whether the
logout event was delivered.onStoreChanged(callback)
Fires when the merchant switches the active store. A merchant can own several stores and
swap between them without reloading the dashboard, so anything your app cached — orders,
products, settings — belongs to the store they just left.
Your iframe reloads immediately after this event, with the new store’s id in the
shop query param. That reload — not this callback — is what boots your app under the new
store, so keep reading shop from the URL on startup exactly as you already do. Treat
this callback as the “the rug is about to be pulled” hook: use it to drop caches that
outlive a reload (IndexedDB, Cache Storage, localStorage) and to save in-progress
work. An app that misses the event still starts up correctly against the right store; it
just loses the chance to flush stale data first.requestPermission(permission, options?, timeoutMs?)
Show the merchant a permission modal. Returns true if approved, false if denied or
timed out. The modal has a 10-second auto-deny countdown.
getGrantedPermissions(timeoutMs?)
Silently retrieve the merchant’s active grants — no modal shown. Call this on wake or
mount to avoid requesting a permission the merchant already approved.
Permission → GrantRecord:
onLaunch()
Ask the dashboard to expand (un-minimize) this app. The call is silently ignored if
AUTO_LAUNCH has not been granted.
pickFiles(options?)
Open the merchant’s Salesive media library and resolve with the files they select. Always
resolves with an array of files (even in single-select mode), or an empty array if
the merchant closes the picker without choosing. The picker only opens while your app is in
the foreground.
size is a human-readable string ("1.2 MB"), not a raw byte count. The returned url
is a hosted, publicly reachable link to the merchant’s existing media — store it or use it
directly.onEvent(type, callback)
Subscribe to any postMessage event by type string — including future event types not yet in
this helper. Callbacks receive the full raw message data object.
APP_EVENTS constants
Complete React example
Environment notes
- Browser only — all functions are no-ops in Node.js / SSR contexts (return
false,{}, or a no-op unsubscribe). Safe to import in Next.js or Remix server components. - No runtime dependencies — the permissions subpath has zero dependencies.
- Single listener — one
window.addEventListener("message", …)is registered on module import. Multiple concurrentrequestPermission()calls resolve independently.
Next steps
AI tools (WebMCP)
Register tools that Ola can call, with merchant approval.
OAuth install flow
Set up the token exchange and session binding.
Scopes & permissions
The API scopes your backend calls need.
Build & publish
Submit your app for marketplace review.
Webhooks
Real-time store events for your backend.

