Skip to content

Turn on the read-only site metadata API and build a launcher for a site's apps.

A site can expose a small, read-only metadata API: a directory of the apps published on that site. Turn it on, and a "root" app can fetch the list and render a launcher, with no backend of its own. It is off by default and enabled per site.

What you get

When enabled, the site serves one document on its own host at /__meta/v1/directory.json.

It lists the site's published apps (an app with no live release does not appear), and looks like this:

json
{
  "version": "v1",
  "site": { "name": "Acme Internal" },
  "apps": [
    { "pathPrefix": "/",     "name": "Home" },
    { "pathPrefix": "/docs", "name": "Docs" }
  ]
}

The endpoint lives behind the site's existing sign-in: every app is gated, so a viewer must be signed in to read it (app names never leak to anonymous visitors). Every signed-in viewer sees every app.

Turn it on

  1. Open the site in the dashboard.
  2. From Actions for {site}, choose Turn on metadata API….
  3. Confirm in the dialog.

The dialog explains what will be served:

This site will serve a read-only directory of its published apps at /__meta/v1/directory.json, behind the site's existing sign-in. Use it to build an app launcher.

Choose Turn off metadata API from the same menu to stop serving it. When you do, the confirmation notes that The /__meta directory will stop being served for this site.

Build the launcher

The launcher is just an ordinary app deployed at the site root (/). On load it fetches the directory and renders a tile per app, each linking to that app's pathPrefix. Because it runs on the same host, the request carries the viewer's session automatically.

js
const res = await fetch("/__meta/v1/directory.json", { credentials: "same-origin" });
if (!res.ok) {
  // Not signed in, or the directory isn't available for this site.
  return;
}
const { site, apps } = await res.json();
// Render `site.name` as a heading and one linked tile per app in `apps`.

Start from a prompt

If you use an AI coding tool, this prompt scaffolds a launcher that matches the contract above. Keep the version pinned to /__meta/v1.

text
Build a single-page static site that acts as an app launcher.

On load, fetch('/__meta/v1/directory.json') on the same origin, sending
credentials so the viewer's session cookie is included. The JSON response is:

  { "version": "v1",
    "site": { "name": "..." },
    "apps": [ { "pathPrefix": "/docs", "name": "Docs" } ] }

Render site.name as a heading and one tile per entry in `apps`, each linking to
its pathPrefix. Show an empty state when `apps` is empty, and an error state if
the fetch fails (a signed-out viewer, or the directory being unavailable).

Output plain static HTML/CSS/JS with no build step and no backend, so it can be
deployed as a static folder and mounted at the site root.

Notes

  • The API is versioned: a future change to the document shape ships under a new path (for example /__meta/v2/...) so an existing launcher keeps working.
  • The directory always reflects published releases, so it never advertises an app that a viewer can't actually open.