Skip to content

Embedded pages ​

An embedded app page gives your app a UI surface inside the host panel: a full tab with your web app rendered in a sandboxed iframe. Use it for dashboards, configuration screens, or any interactive UI that goes beyond what settings and metafields alone can provide.

Declare an app page ​

Add app_page.url to your manifest:

jsonc
"app_page": {
  "url": "https://app.example.com/stayblox"
}

When the app is installed, Stayblox adds a {App Name} entry to the host's panel navigation. Clicking it renders your URL in a sandboxed iframe, with a signed session token appended as a query parameter:

https://app.example.com/stayblox?session=eyJhbGciOiJIUzI1NiJ9...

The session token ​

The session query parameter is a HS256-signed JWT. Verify it on your server to confirm the request comes from Stayblox and to identify the installing host.

Token structure ​

json
{
  "iss": "stayblox",
  "aud": "your_client_id",
  "team_id": 42,
  "user_id": 7,
  "install_id": 19,
  "iat": 1749812734,
  "exp": 1749813034
}
ClaimDescription
issAlways "stayblox".
audYour app's client_id. Verify this matches your own credential.
team_idThe host's team ID. Use this to load team-specific data from your own store, or scope API calls.
user_idThe panel user who opened the app page.
install_idThe install's ID. Useful for associating your own session with the Stayblox install record.
iatIssued-at unix timestamp.
expExpiry unix timestamp. 5 minutes from issuance.

Signing key ​

The token is signed with your app's client_secret using HS256. Verify it with the same key.

Verification examples ​

Node.js (using jsonwebtoken)

js
import jwt from 'jsonwebtoken'

function verifySession(token) {
  // Throws if the token is invalid, expired, or signed with the wrong key.
  const payload = jwt.verify(token, process.env.STAYBLOX_CLIENT_SECRET, {
    algorithms: ['HS256'],
    issuer: 'stayblox',
    audience: process.env.STAYBLOX_CLIENT_ID,
  })

  return payload // { team_id, user_id, install_id, ... }
}

PHP (using firebase/php-jwt)

php
use Firebase\JWT\JWT;
use Firebase\JWT\Key;

function verifySession(string $token): object
{
    // Throws on invalid signature, expiry, wrong issuer/audience.
    return JWT::decode(
        $token,
        new Key($_ENV['STAYBLOX_CLIENT_SECRET'], 'HS256'),
    );
    // Verify aud separately if needed:
    // assert($payload->aud === $_ENV['STAYBLOX_CLIENT_ID']);
}

Always:

  • Verify the signature before trusting any claim.
  • Check exp to reject expired tokens (most JWT libraries do this automatically).
  • Check aud matches your own client_id.

Token refresh ​

The session token has a 5-minute TTL. For long-lived app pages (dashboards the host keeps open), use the postMessage token-refresh bridge to get a fresh token without a full page reload.

How it works ​

 Your iframe                        Stayblox parent frame
      │                                      │
      │── postMessage({type:'stayblox:refresh-token'}) ──▶│
      │                                      │ mints a new JWT
      │◀── postMessage({type:'stayblox:token', token: '...'}) ──│
      │                                      │

Your page posts a message to the parent; Stayblox mints a fresh token and replies to your declared origin only.

Client-side implementation ​

js
// Request a new token from the Stayblox parent frame.
function refreshSession() {
  return new Promise((resolve, reject) => {
    const timeout = setTimeout(() => reject(new Error('Token refresh timed out')), 5000)

    function handler(event) {
      // Only accept messages from the Stayblox app domain.
      if (event.origin !== 'https://admin.stayblox.com') return
      if (event.data?.type !== 'stayblox:token') return

      clearTimeout(timeout)
      window.removeEventListener('message', handler)
      resolve(event.data.token)
    }

    window.addEventListener('message', handler)
    window.parent.postMessage({ type: 'stayblox:refresh-token' }, 'https://admin.stayblox.com')
  })
}

// Example: refresh before an API call when you know the token is near expiry.
async function callMyApi(endpoint) {
  const token = await refreshSession()
  return fetch(endpoint, {
    headers: { 'X-Stayblox-Session': token }
  })
}

Proactive refresh

Rather than waiting for a 401 from your backend, decode the exp claim from the initial token on page load and schedule a refresh 30–60 seconds before expiry with setTimeout. This avoids any window where the host triggers an action with an expired session.

Sandbox and security ​

The iframe is rendered with sandbox attributes that restrict what it can do:

html
<iframe
  src="https://app.example.com/stayblox?session=..."
  sandbox="allow-scripts allow-same-origin allow-forms allow-popups"
></iframe>

In addition, the platform's Content Security Policy pins frame-src to your declared app_page.url origin, so the iframe URL cannot be substituted.

Design your app page to work within these constraints:

  • JavaScript execution is allowed (allow-scripts).
  • Popups for OAuth sub-flows are allowed (allow-popups).
  • Form submission is allowed (allow-forms).
  • Top-level navigation from within the iframe is not allowed. If you need to send the user somewhere else, open it in a new tab with target="_blank".

Loading and error states ​

Your page is responsible for its own loading and error UI. The iframe is rendered at full panel width; Stayblox doesn't inject any loading overlay.

A good first-render pattern:

  1. Parse ?session=... from the URL on load.
  2. Verify the JWT on your server (exchange it for your own session token if needed).
  3. Render a spinner while the exchange is in flight.
  4. On verification failure (bad signature, expired token), show a clear error and a "Reload" button that re-opens the panel page (which mints a fresh JWT).

Checklist ​

  • [ ] app_page.url is an https:// URL in the manifest.
  • [ ] Server verifies the session JWT signature and exp before acting on it.
  • [ ] Server checks aud matches your own client_id.
  • [ ] Client-side implements token refresh via the postMessage bridge for long-lived pages.
  • [ ] UI degrades gracefully when the token is missing or expired.

© Stayblox — Developer Platform