Build an app that is only a page.

A page app signs in and calls Hello Purple from the page itself, with no server of yours, so an AI builder or any static host can publish it. It works on demo accounts only.

For now, an app works only for the person who registered it. No one else can use it, not even with their own account.

You need a browser, a builder or host that publishes the page at its own https address, and a staging account with a supported broker (create one at Axiory).

  1. Sign in to My Apps and register the app. Where it asks where the app runs, choose “My app is only a page, with no server of mine”. Leave the redirect URLs empty if the page has no address yet.
  2. When the API key is shown, choose “Copy the values for your page” beside it. They include the key, which My Apps shows only once.
  3. Paste the values into your builder’s chat as they are. They tell it to follow this page.
  4. Publish the page, and note its address, such as https://my-app.example/.
  5. In My Apps, open the app, choose Edit sign-in URLs and add that address exactly, closing slash included. Within about a minute the page can read the app’s answers.
  6. Open the page and sign in with the account that registered the app. It shows your demo data.

About a day later the page asks you to sign in again. That is expected.

Using Claude Code on your own computer? It builds the page for you, keeping the same rules.

For agents: this page as Markdown.

For your builder

The brief it follows, with the values for this environment

The person wants a Hello Purple app that is only a page, built by you, in the test environment plt-1455.

You are done when the page, on its published address, signs the person in, shows one demo read, renews its sign-in and signs out, with no server of its own.

Before you build. A builder that publishes a static page: follow this page. An app with a server: start at https://portal-plt-1455.murban.personal.purple-lab.dev/start/.

  • Copy the browser template unchanged and call only through it.
  • Never log, print or send a token or the key anywhere else.
  • Never click through sign-in, consent or terms for the person.
  • Load no third-party script, and host the page on an origin no one else’s pages share.

Values for this environment

  • API base, apiBaseUrl: https://purple-engine-axi-plt-1455.murban.personal.purple-lab.dev
  • Hub, hubOrigin: https://purple-engine-plt-1455-murban.auth.eu-central-1.amazoncognito.com
  • Browser template: https://portal-plt-1455.murban.personal.purple-lab.dev/start/browser/engine-browser.mjs
  • From My Apps, in the values the person gives you: clientId, appId, ownerBrandUserId, identityProvider, hubOrigin, apiBaseUrl, scopes and apiKey. Each is the template’s option of the same name.

Procedure

  1. Add the browser template to the page’s source as engine-browser.mjs, unchanged.
  2. Create one session with createEngineBrowser, passing each value by its name. Pass scopes as a list: split the value on spaces. Pass redirectUri as the page’s own root, location.origin + "/".
  3. On every page load, call completeSignIn() before anything else. While signedIn is false, show a sign-in button that calls signIn().
  4. Make every call with the session’s fetch, such as fetch('/v1/trading-account/demo/accounts'), and show the result. Give the page a sign-out button that calls signOut().
  5. Show an EngineBrowserError’s message as it is: it names no token or key.
  6. Add the policy tag from the rules to the page’s head, publish the page, and tell the person its address, so they add it in My Apps.

The rules the page keeps

  • Make every engine call through the template. Copy it into the page’s source unchanged, as engine-browser.mjs, from https://portal-plt-1455.murban.personal.purple-lab.dev/start/browser/engine-browser.mjs. It holds the sign-in, its renewal and every call, and imports nothing.
  • The template keeps the sign-in in the tab’s sessionStorage, never localStorage, so it ends with the tab. It runs one renewal at a time and stores each new refresh token before it uses the new access token, so add no renewal of your own.
  • The template sends only the headers the API allows from a page: Authorization, x-api-key, Content-Type, Idempotency-Key and X-Retrieval-Token. It refuses any other, which the browser would report only as a network failure.
  • A refused call looks like a network failure in the page. The template renews and retries once, with the same Idempotency-Key on a write. When the call still fails, the person reads the refusal as “Before you replay a call” says.
  • The app is a browser app, so its key is in the page as a public identifier: put it into the page’s build as the person gave it, for Vite as VITE_ENGINE_API_KEY. It opens nothing without the person’s sign-in.
  • The page signs in at its own address, the site root: https:// on its published host, or http://127.0.0.1:<port>/ for a preview, never localhost. Preview on 127.0.0.1 with a fixed port, the one registered. Another port or another subdomain, a builder’s preview included, is another origin, and its calls fail until the person adds that address in My Apps too.
  • One author per origin. Every script on the page’s origin can read the sign-in, so host the page where nobody else’s pages share its origin.
  • The app’s refresh token is issued for 24 hours, so the person signs in again about once a day.
  • Rotating the key in My Apps breaks every published copy of the page until it is rebuilt with the new one. For a page that may be compromised, revoke rather than rotate.
  • Removing an address from the app stops pages there from reading the API's answers within about a minute; it does not stop anyone holding a sign-in from calling from elsewhere until it lapses. Revoking the app does.

Give the page a Content Security Policy that allows scripts from its own origin only and connections to the hub and the API only. For this environment, put this tag in the page’s head:

<meta http-equiv="Content-Security-Policy" content="script-src 'self'; connect-src https://purple-engine-plt-1455-murban.auth.eu-central-1.amazoncognito.com https://purple-engine-axi-plt-1455.murban.personal.purple-lab.dev; object-src 'none'; base-uri 'none'">

Also

  • An app that needs a server, such as one that takes callbacks or trades on a broker credential the platform issues, is not a page app. Send the person to https://portal-plt-1455.murban.personal.purple-lab.dev/start/.
  • Every trading-account permission is a demo one. Live accounts are out of reach.
  • An agent with the Hello Purple plugin builds the same page through its safe-secret-handling skill.

Before you replay a call

What to confirm in My Apps when the page cannot read a call

A page cannot read a call the gateway refused: the gateway answers before the API runs the call, so the page sees a network failure with no status. The template renews and retries once. If the call still fails, confirm in My Apps that:

  • the app is a browser app;
  • the page’s exact address is one of its redirect URLs, with its origin shown beside it;
  • the API key in the page’s build is the app’s current key;
  • the account signed in on the page is the one that registered the app.

Then replay the call outside the browser: in the developer tools’ network panel, use Copy as cURL on the failed request, run it in a terminal, and read the status and the code in the response. The copied command carries the page’s access token and the API key, so run it yourself and share none of it.

An agent with the Hello Purple plugin reads the same refusal with its authorize script’s check mode, which shows it no token and no key.