PassPort app launch & app-entry

When a user opens your registered application, PassPort decides where it should land based on that user’s account state — a not-yet-set-up account can be routed to an onboarding screen, an active account straight into the app. This page covers declaring your app’s launch surface at registration and resolving the entry point at runtime.

All paths are under the public gateway at https://api.flexgalaxy.ai.

Declare your launch surface when you register

Register (or update) a service with POST /developer/v1/services. The service manifest’s surfaces.app_launch describes how your app is opened. Paths are relative — the platform resolves them against your app origin.

{
  "surfaces": {
    "app_launch": {
      "allowed_redirect_paths": ["/", "/auth/callback"],
      "launch_path": "/",
      "required_action": "sampleApp:Open",
      "resource_frn_pattern": "frn:${account_id}:sample-app:app/*",
      "onboarding_entry_url": "/welcome"
    }
  }
}
  • allowed_redirect_paths — the permitted post-sign-in return paths. Your app’s homepage is derived from the first entry that is not a /passport/… callback.

  • launch_path / required_action / resource_frn_pattern — the launch target and the authorization action + resource pattern checked when the app opens.

  • onboarding_entry_url (optional) — a distinct entry for accounts that are not yet active (see below). Omit it and every account opens at the homepage.

A newly registered service is reviewed and approved before it becomes launchable; until then app-entry resolution will not find an active application for it.

Resolve the entry point at runtime

On Open, call:

POST /passport/v1/app-entry/resolve

{ "appId": "<application id>", "accountId": "<account uuid>" }

Both fields are required. Note that the PassPort surface is camelCase, unlike the snake_case organization and account APIs.

accountId is not taken on trust: PassPort verifies that the account holds an active installation of the application, that your token was issued in that account’s organization realm, and that you are an assigned workforce member of that account, before it resolves anything.

The response tells you where to send the authenticated principal, keyed off that account’s lifecycle state:

{
  "appId": "<application id>",
  "accountId": "<account uuid>",
  "entryKind": "ONBOARDING",
  "deepLink": "/welcome?account_id=<account uuid>",
  "lifecycleStatus": "PLACEHOLDER",
  "displayName": "…"
}

Account lifecycle status

entryKind

Where deepLink points

PLACEHOLDER / MATERIALIZING

ONBOARDING

the app’s onboarding_entry_url, or the homepage if none was declared

ACTIVE

APP

the app homepage / launch path

no lifecycle record (an ordinary, non-onboarded account)

APP

the app homepage / launch path

CLOSED

—

409 account_closed; a closed account has no entry

deepLink always carries the resolved account as an account_id query parameter, appended with & when the configured URL already has a query string. lifecycleStatus is omitted for an account with no lifecycle record.

This is a signed-in (workforce / session) call — drive it from your app’s authenticated front end. An AK/SK service credential is rejected with 401.

Look up an application’s public metadata with:

GET /passport/v1/applications/{appId}

Orgs: org-scoped entry for org-shaped apps

Some registered applications are org-scoped: a tenant enters through an enterprise org rather than through an account, and the app serves one site per org. A deep link to such an app pins the org, and PassPort resolves an entry for that org and nothing else — there is no fallback to another org the caller also belongs to, and no console/StartPoint access is reachable from an app-entry flow.

The resolve call takes orgId instead of accountId — exactly one of the two is required (400 entry_context_required otherwise):

POST /passport/v1/app-entry/resolve

{ "appId": "<application id>", "orgId": "<org uuid>" }

Semantics:

  • The org is pinned. The entry is resolved through the org’s management account, and the returned deep link is built only from the requested org.

  • The same guard chain as account entry applies: the application must be active, the app must have an active installation on the org’s management account, your token must be issued in the org’s entry realm — the realm the public discovery endpoint below returns, which is the platform realm flexgalaxy for every org today — and you must be an assigned workforce member of the management account. The context in the request is never trusted as an unchecked claim.

  • The management account’s lifecycle status maps to entryKind exactly as in the table above; a CLOSED org has no entry (409 account_closed).

  • deepLink is the org’s registered site URL with the org id substituted for the {orgId} placeholder — it lands you inside the org’s site.

An org launch shape is declared at registration: the app registers a site URL template containing exactly one {orgId} placeholder. An org entry cannot resolve without it — a malformed registration is a clean 403 application_unavailable, never a silent fallback to an account-scoped or cross-org entry.

The response carries the resolved context:

{
  "appId": "<application id>",
  "accountId": "<management account uuid>",
  "orgId": "<org uuid>",
  "entryKind": "APP",
  "deepLink": "https://site.example/orgs/<org uuid>",
  "lifecycleStatus": "ACTIVE",
  "displayName": "…"
}

orgId is present only on the org-scoped path; accountId is the management account the entry was resolved through.

Feed the org picker: orgs you can enter

POST /passport/v1/app-entry/orgs

{ "appId": "<application id>" }

returns the orgs the caller may enter this application through — assigned orgs only (the workforce assignment projection), filtered to orgs that carry an active installation on their management account, and bound to the caller’s own token realm:

{
  "orgs": [
    { "orgId": "<org uuid>", "orgName": "…", "issuer": "https://…/realms/<realm>" }
  ]
}

A caller assigned to exactly one such org can be sent straight through the org-scoped resolve; with several, the front end renders a picker. issuer is the entry realm’s token issuer (the platform realm’s, for every org today), so the feed is self-sufficient — a front end never computes realm data.

See also