Skip to main content
Your app can have a page inside the Tassyir dashboard, next to the merchant’s orders and products. The dashboard shows it in a frame and talks to it through App Bridge: your page asks for ID tokens, shows toasts and modals, uses the dashboard’s save bar and title bar, navigates, and opens the dashboard’s pickers. @tassyir/app-bridge is the page’s side of it.

How the page is loaded

Set admin.page in the manifest (usually /). When the merchant opens your app, the dashboard address is https://app.tassyir.io/{locale}/dashboard/{businessId}/apps/{handle}/{path}, and your frame loads:
hosting.url + admin.page + the app path ({path}) + the dashboard’s own query string
So every path below admin.page is your page: serve the same document for all of them and route on the client.
  • Allow the dashboard to frame your page, and nothing else: Content-Security-Policy: frame-ancestors https://app.tassyir.io.
  • The page counts as shown at its frame’s load, or at its first App Bridge call. With neither within 15 seconds, the dashboard shows a “didn’t load” state with Try again.
  • Tassyir vouches for nothing in your page’s address. Never read a business, a user or a store from it to decide what to show or allow. App Bridge’s context is for display only. Who is looking is what a verified ID token says (Tokens).
  • The dashboard never gives your page a Tassyir credential: the ID token is the only one. Your frame’s cookies are third-party cookies, which browsers often block: do not build a session on them.

A page

browser
Your server verifies each token with tassyir.authenticate(idToken) before it answers (Tokens).

Calls

Every call posts one message to the dashboard and resolves with its answer, or rejects with an AppBridgeError. The SDK checks each payload before posting, so most mistakes are refused without a round trip.
  • Text (toasts, modal and page titles) is shown as text: control and bidi characters become spaces, white space is folded, and it is cut (150 characters for a toast, 80 for a title). More than 2,000 characters is refused.
  • App paths (navigate, modal.open) are relative to admin.page and start with one /, with an optional query: /reviews/2?tab=sent. No scheme or host, backslash, white space, control or bidi character, fragment, or ., .. or empty segment. At most 1,024 characters before the ? and 2,048 in all.
  • navigate({ path }) does not reload your frame: show the page yourself (with your router), and the dashboard’s address follows, as a new history entry or, with replace: true, in place. Back and Forward reload your frame at the address they land on.
  • navigate({ dashboard }) opens home, orders, order (with id), customers, customer (with id), tracking, inventory, products (the current store’s) or apps.
  • A modal’s page may call only idToken, toast.show, modal.close, resource.pick and context.get. One modal and one picker at a time.
  • modal.open and resource.pick wait for the merchant as long as the dialog is open. Every other call times out after 10 seconds, and every call takes an AbortSignal.

The save bar

Show the dashboard’s save bar while your page has unsaved changes. Its buttons send events to your page:
browser
The bar does not stop the merchant from leaving the page. When the page goes away, the SDK hides the bar and clears the title, so neither outlives it.

Pickers

resource.pick resolves when the merchant closes the dashboard’s picker: selection is empty when they cancelled, holds one item unless multiple is true, and at most 50. The person looking must also be allowed to read what the picker shows. An installation limited to selected stores picks from the current store only, which must be one of its stores.

Context

browser
The context is { locale: 'ar' | 'en', dir: 'rtl' | 'ltr', theme: 'light' | 'dark', storeId }. storeId is the dashboard’s current store, or null when there is none or your installation is limited to other stores. Follow locale, dir and theme so your page reads like the dashboard: Arabic pages run right to left. The context is for display; it proves nothing.

Errors

An AppBridgeError has a stable code, the call that failed, a source (dashboard or sdk), a message for people and a fix. isAppBridgeError(error, code?) narrows an unknown error. The @tassyir/app-bridge README lists the SDK’s other codes.

Limits

The dashboard counts calls per installation over a sliding minute, and answers RATE_LIMITED past a call’s limit. Past 240 messages a minute in all, it drops messages without answering. The SDK counts what your page sends and refuses first, so your page sees RATE_LIMITED rather than a timeout. A reloaded frame does not reset them: they last as long as the dashboard tab.