Applications

Build interactive web applications that integrate with Orbit's membership system. The Orbit Connector SDK lets your apps identify the current user, check their membership status, and store per-user data — so you can build tools with tiered functionality where members get more than guests.

For a non-technical overview of app resources, see Resources > Supported Types.

Overview

App resources are web applications embedded directly in your Orbit resource library. When a user views an app resource, the application loads on the page in a seamless embedded view. Apps can also be opened in a new tab.

The key differentiator from a regular link is that apps can talk to Orbit — they know who the user is, whether they're a member, and can save/load data tied to each user.

What You Can Build

  • Calculators and tools — financial planners, ROI calculators, assessment tools
  • Interactive dashboards — data visualizations, progress trackers
  • Quizzes and assessments — with results saved per user
  • Configurators — product selectors, plan comparison tools
  • Mini-apps — anything your members need, with member-only features gated behind membership status

Building Tools

Any web application served over HTTPS works as an app resource. Popular approaches:

  • AI app buildersLovable, Bolt, v0, Replit, Claude Artifacts — generate a hosted web app from a description
  • Static sites — HTML/CSS/JS on Netlify, Vercel, GitHub Pages, or any hosting
  • Full-stack apps — Next.js, React, Vue, Svelte, or any framework

Adding an App to Orbit

  1. Go to Resources in the admin portal
  2. Click Add Resource and select the App tab
  3. Paste the URL of your web application (e.g., https://my-calculator.lovable.app)
  4. The resource is created with the app embedded on its page

You can also add an App URL to any existing resource by editing it and entering the URL in the Embedded App section.

The resource's slug becomes the app's identifier (appId) throughout the SDK. For example, a resource with slug fire-calculator is referenced as appId: "fire-calculator".

Orbit Connector SDK

The SDK is a small JavaScript file that your app loads to communicate with Orbit. It handles authentication automatically across all runtime modes.

Installation

Add the SDK via a script tag in your app's HTML:

<script src="https://orbitams.com/static/js/orbit-connector.js" defer></script>

The script creates a global OrbitConnector object on window.

Initialization

const orbit = OrbitConnector.create();
await orbit.init();

That's the minimum required. The SDK auto-detects its environment and connects to Orbit.

Configuration Options

const orbit = OrbitConnector.create({
  // The URL of your Orbit portal. Required in development mode,
  // auto-detected when embedded or launched from Orbit.
  origin: 'https://your-portal.orbitams.com',

  // Your app's identifier (the resource slug in Orbit).
  // Auto-detected when embedded, required for state operations in other modes.
  appId: 'my-app-slug',

  // Force a specific mode instead of auto-detecting.
  // Usually you should leave this as 'auto'.
  mode: 'auto', // 'auto' | 'iframe' | 'external' | 'development'

  // Enable console logging for debugging.
  debug: false,
});

Runtime Modes

The SDK operates in three modes, auto-detected based on the environment. Your app code stays the same — the SDK handles the differences internally.

Iframe Mode (Embedded in Orbit)

When your app is embedded on an Orbit resource page, the SDK communicates with the parent page via postMessage. The user is already authenticated in Orbit, so no login is needed.

How it works: The parent page (Orbit) and your app perform a handshake: your app sends orbit:hello, Orbit responds with orbit:hello:ack containing the app's ID and capabilities. Subsequent requests are sent as orbit:request messages and proxied by the parent page to Orbit's internal bridge API using the user's existing session cookie.

No configuration needed — origin and appId are provided by the parent page during the handshake.

External Mode (New Tab from Orbit)

When a user opens your app in a new tab from Orbit, a short-lived token is passed in the URL (?orbit_token=...&orbit_origin=...). The SDK uses this token for direct API calls.

No configuration needed — the token and origin are read from URL parameters.

Development Mode (Standalone / Builder Preview)

When running outside Orbit — in a Lovable preview, Bolt workspace, v0 editor, or localhost — the SDK opens a popup window for the user to log in to Orbit, then receives a session token.

Requires origin configuration — since the SDK can't detect which Orbit portal to connect to. There are three ways to provide it:

Option 1: Query parameters (recommended for testing)

Add orbit_origin and optionally orbit_app_id to the URL:

https://my-app.lovable.app/?orbit_origin=https://your-portal.orbitams.com&orbit_app_id=my-app-slug

This is the fastest way to test — no code changes needed. The SDK reads these parameters automatically.

Option 2: SDK configuration (recommended for development)

const orbit = OrbitConnector.create({
  origin: 'https://your-portal.orbitams.com',
  appId: 'my-app-slug',  // enables state storage
  debug: true,
});

Option 3: Environment variables

Set NEXT_PUBLIC_ORBIT_ORIGIN and NEXT_PUBLIC_ORBIT_APP_ID (or equivalent for your framework) and read them in your initialization code.

Console guidance: When debug: true is set, the SDK logs detailed setup instructions to the browser console. If origin is missing, the SDK prints the exact steps to configure it. If appId is missing, it explains how to enable state storage. This makes the SDK easy to integrate from AI app builders — the console output guides the agent through setup automatically.

The popup flow: 1. SDK opens a popup to https://your-portal.orbitams.com/resources/connect/auth/ 2. If the user isn't logged in, they see the Orbit login page 3. After authentication, Orbit generates a signed session token (valid 24 hours) 4. The token is posted back to your app via postMessage and the popup closes 5. The token is cached in sessionStorage for the browser session

SDK API Reference

orbit.getMember()

Returns information about the current user, their membership, and the tenant.

const ctx = await orbit.getMember();

Response:

{
  user: {
    id: "uuid-string",
    email: "user@example.com",
    firstName: "Jane",
    lastName: "Doe"
  },
  tenant: {
    name: "My Organization",
    origin: "https://my-org.orbitams.com"
  },
  member: {
    isAuthenticated: true,
    roles: ["MEMBER"],
    membership: {
      active: true,
      status: "active",
      planName: "Professional",
      startsAt: "2024-01-15T00:00:00",
      endsAt: "2025-01-15T00:00:00"
    }
  },
  app: {
    appId: "fire-calculator"
  }
}

Common patterns:

// Check if user is an active member
const isMember = ctx.member?.membership?.active === true;

// Get the user's plan
const plan = ctx.member?.membership?.planName;

// Check if authenticated at all
const isLoggedIn = ctx.member?.isAuthenticated === true;

orbit.state.get(key)

Retrieve stored data for the current user and app.

const result = await orbit.state.get('my-key');
// result: { key: "my-key", value: { ... }, updatedAt: "2024-..." } or null
  • key defaults to 'default' if omitted
  • Returns null if no data exists for that key
  • Requires appId to be resolved (automatic when embedded)

orbit.state.set(key, value)

Save data for the current user and app.

await orbit.state.set('preferences', { theme: 'dark', fontSize: 16 });
await orbit.state.set('progress', { level: 3, score: 850 });
  • value can be any JSON-serializable data (objects, arrays, strings, numbers, booleans)
  • Maximum value size: 64 KB per key (after JSON serialization)
  • Maximum keys: 10 per user per app
  • Creates the key if it doesn't exist, updates if it does

orbit.state.delete(key)

Remove stored data.

await orbit.state.delete('preferences');

orbit.ui.resize(height, options?)

Set the iframe height in pixels. Call this after your app renders to ensure the iframe fits your content. Only works in iframe mode — in other modes this is a no-op.

// Set height — iframe scrollbar is hidden by default for a seamless look
await orbit.ui.resize(800);

// Keep the iframe scrollbar if the content may exceed the height
await orbit.ui.resize(600, { overflow: 'scroll' });

Options: - overflow: 'hidden' (default) removes the iframe scrollbar for a seamless embedded experience. 'scroll' keeps the scrollbar.

Tip: Call resize after your content renders or changes (e.g., after loading data, expanding sections). A common React pattern:

useEffect(() => {
  if (orbit?.getConnectionStatus().mode === 'iframe') {
    requestAnimationFrame(() => {
      orbit.ui.resize(document.body.scrollHeight);
    });
  }
}); // runs after every render

orbit.ui.openExternal(url)

Open a URL in a new browser tab.

orbit.ui.openExternal('https://example.com');

orbit.getConnectionStatus()

Check the current connection state. Useful for displaying connection indicators in your UI.

const status = orbit.getConnectionStatus();

Response:

{
  mode: "iframe",           // "iframe" | "external" | "development" | "unknown"
  transport: "iframe",      // "iframe" | "api" | "none"
  originResolved: true,
  appIdResolved: true,
  capabilities: {
    member: true,           // Can call getMember()
    access: true,           // Can check membership
    storage: true,          // Can use state.get/set/delete
    iframeBridge: true      // Using iframe postMessage transport
  }
}

Building an App: Step by Step

Here's a complete walkthrough for building an app that integrates with Orbit.

1. Create Your Web App

Use any tool or framework. Here's a minimal example:

<!DOCTYPE html>
<html>
<head>
  <title>My Orbit App</title>
  <script src="https://orbitams.com/static/js/orbit-connector.js" defer></script>
</head>
<body>
  <div id="app">Loading...</div>

  <script>
    async function main() {
      const app = document.getElementById('app');

      // Initialize the SDK
      const orbit = OrbitConnector.create({
        origin: 'https://your-portal.orbitams.com', // only needed during development
        debug: true,
      });

      try {
        await orbit.init();
        const ctx = await orbit.getMember();

        if (ctx.member?.membership?.active) {
          app.innerHTML = `<h1>Welcome, ${ctx.user.firstName}!</h1>
                           <p>Your plan: ${ctx.member.membership.planName}</p>`;
        } else {
          app.innerHTML = `<h1>Welcome!</h1>
                           <p>Join as a member to unlock premium features.</p>`;
        }
      } catch (e) {
        // Not connected to Orbit — show standalone mode
        app.innerHTML = `<h1>Welcome!</h1>
                         <p>Running in standalone mode.</p>`;
      }
    }

    // Wait for SDK script to load
    if (window.OrbitConnector) {
      main();
    } else {
      document.querySelector('script[src*="orbit-connector"]')
        .addEventListener('load', main);
    }
  </script>
</body>
</html>

2. Using AI App Builders

When building with Lovable, Bolt, v0, or Replit, you can give the AI agent Orbit's integration guide directly. This is a machine-readable guide designed for AI agents, hosted at:

https://orbitams.com/llms.txt

Option A — Point the agent to the guide (recommended):

Read the integration guide at https://orbitams.com/llms.txt and use it to integrate this app with Orbit AMS. My portal domain is myorg.orbitams.com.

Replace myorg.orbitams.com with your actual portal domain — this is the domain you see in your browser when logged in to Orbit (e.g., myorg.orbitams.com or a custom domain like members.example.com). The guide will instruct the agent to use this domain for the SDK origin config.

Option B — Include instructions directly in your prompt:

This app integrates with Orbit AMS. My portal domain is myorg.orbitams.com. Load the Orbit Connector SDK via <script src="https://orbitams.com/static/js/orbit-connector.js" defer></script> in the HTML head. Initialize with const orbit = OrbitConnector.create({ origin: 'https://myorg.orbitams.com', debug: true }); await orbit.init();. Use orbit.getMember() to check if the user is a member. Use orbit.state.get(key) and orbit.state.set(key, value) to persist data. When embedded in Orbit: use full width layout, hide the app title (Orbit shows it), and use a white or transparent background. Check the browser console for setup guidance.

Replace myorg.orbitams.com with your actual portal domain. Note that the SDK script tag always loads from orbitams.com — only the origin config needs your portal domain.

The debug: true flag is especially useful — the SDK prints actionable setup instructions to the browser console, which helps AI agents resolve configuration issues automatically.

3. Implement Permission Gating

A common pattern — show different features based on membership:

const ctx = await orbit.getMember();
const isMember = ctx.member?.membership?.active === true;

// Show/hide UI elements
document.querySelectorAll('[data-member-only]').forEach(el => {
  el.style.display = isMember ? '' : 'none';
});

document.querySelectorAll('[data-guest-only]').forEach(el => {
  el.style.display = isMember ? 'none' : '';
});

Or with React:

function App() {
  const [member, setMember] = useState(null);

  useEffect(() => {
    async function connect() {
      const orbit = OrbitConnector.create();
      await orbit.init();
      const ctx = await orbit.getMember();
      setMember(ctx);
    }
    connect().catch(() => setMember(false));
  }, []);

  const isMember = member?.member?.membership?.active;

  return (
    <div>
      <BasicFeature />
      {isMember ? <PremiumFeature /> : <UpgradePrompt />}
    </div>
  );
}

4. Save and Load User Data

Store settings, progress, or results per user:

// Save
await orbit.state.set('settings', {
  theme: 'dark',
  notifications: true,
});

// Load
const result = await orbit.state.get('settings');
if (result) {
  applySettings(result.value);
}

// Save multiple keys (up to 10 per user per app)
await orbit.state.set('progress', { level: 5, score: 1200 });
await orbit.state.set('preferences', { language: 'en' });

// Delete
await orbit.state.delete('progress');

Storage limits: - Up to 10 keys per user per app - Up to 64 KB per value (after JSON serialization) - Data is stored server-side and persists across sessions and devices

5. Test During Development

Standalone (no Orbit connection): Just open your app in a browser. SDK initialization will fail gracefully — wrap it in a try/catch and show a standalone experience.

Connected to local Orbit: 1. Start the Orbit dev server: poetry run python manage.py runserver 2. Build frontend assets: npm run build (compiles orbit-connector.js) 3. Create an app resource in the admin portal with your app's URL (e.g., http://localhost:3000) 4. View the resource — your app loads in the iframe with full SDK access

Connected to production Orbit: Set origin to your production portal URL. The SDK opens a login popup when running in development mode.

6. Deploy and Add to Orbit

  1. Deploy your app to any hosting service (Vercel, Netlify, your own server)
  2. In the Orbit admin portal, create a resource and set the App URL to your deployed URL
  3. Users can now access your app from the resource library

Important: If your app uses the SDK in development mode with a hardcoded origin, you can safely remove it before deploying — when embedded in Orbit, origin and appId are detected automatically.

App Connector REST API

For advanced use cases, you can call the App Connector API directly instead of using the SDK.

Authentication

App connector requests use Bearer token authentication:

Authorization: Bearer <app-connector-token>

Tokens are short-lived and signed. They're generated automatically by the SDK — you shouldn't need to manage tokens manually in most cases.

Endpoints

Base URL: https://your-portal.orbitams.com/api/apps/

GET /api/apps/context

Get the current user's identity, membership status, and tenant info.

Query parameters: - app_id (optional) — the app's resource slug

Response:

{
  "user": {
    "id": "uuid",
    "email": "user@example.com",
    "first_name": "Jane",
    "last_name": "Doe"
  },
  "tenant": {
    "name": "My Organization",
    "origin": "https://my-org.orbitams.com"
  },
  "member": {
    "is_authenticated": true,
    "roles": ["MEMBER"],
    "membership": {
      "active": true,
      "status": "active",
      "plan_name": "Professional",
      "starts_at": "2024-01-15",
      "ends_at": "2025-01-15"
    }
  },
  "app": { "app_id": "fire-calculator" }
}

GET /api/apps/state?app_id=<slug>&key=<key>

Get stored state. Returns 404 if no state exists for the key.

PUT /api/apps/state?app_id=<slug>

Save state. Body: { "key": "my-key", "value": { ... } }

DELETE /api/apps/state?app_id=<slug>&key=<key>

Delete stored state.

Example App

A complete example application (a FIRE/investment calculator) is included in the Orbit source at resources/docs/example-app/. It demonstrates:

  • Free features available to all visitors (investment projections)
  • Member-only features (cost of living analysis)
  • Per-user data storage (saved scenarios)
  • Connection status display
  • Graceful standalone fallback

See the example app README for setup instructions.

See how Orbit can work for you

Get a personalized walkthrough and see how associations like yours are simplifying their operations.

Try Orbit Free

Get tips on running a better association — delivered to your inbox.

Stay as long as you’d like. Unsubscribe anytime.