Steward
All docs

Docs Running a business

Analytics

Analytics in the sidebar shows each business you own: revenue, signups, visits, clicks and conversion over 7, 30 or 90 days, then a product panel for each business with what happens inside its own app.

Numbers from the hosted page

The studio counts these itself, with no scripts:

Product events from your app

To see what people do inside the app itself, send events.

  1. On Analytics, open Set up events under the business and press Make a write key. Copy it right away. It isn't shown again, because only a hash of it is kept. New write key replaces it, and the old one stops working.
  2. Add the snippet to your app's pages: ``html <script src="https://<your hub>/e/<slug>/snippet.js" data-key="ek_..." defer></script> ` It sends a visit on each page load. Send your own events with steward.track("trial_started", { plan: "pro" }). After sign-in, call steward.identify(hash)` with a hash of the user's id (for example SHA-256 in hex), so one person counts once across devices. Never pass an email or a raw id; the hub refuses anything that isn't a hash.
  3. Browsers may only send from the business's own sites: the site set on Releases, the health check URL on Integrations, and any sites you add under More sites. Requests from anywhere else are refused.

From a server (Bun or Node 18+), send the key in a header:

await fetch("https://<your hub>/e/<slug>", {
  method: "POST",
  headers: { "content-type": "application/json", "x-steward-key": process.env.STEWARD_WRITE_KEY },
  body: JSON.stringify({ name: "paid", anonymousId: user.anonymousId, userId: sha256(user.id), properties: { plan: "pro" },
    context: { userAgent: req.headers.get("user-agent") } }),
});

Pass the visitor's user agent in context.userAgent if you have it, so bots are filtered for server events too.

The event

FieldRule
nameLetters, numbers, _ . : -, up to 60. Lowercased.
anonymousId8 to 64 letters, numbers, _ or -. The snippet makes one per browser.
userIdOptional. A hex hash (16 to 128 characters) of your user's id.
propertiesOptional. Up to 20 keys; strings (cut at 200 characters), numbers or true/false; 1 KB in all. A path loses its query string. A ref is kept for attribution.

The whole request is at most 4 KB. The answer is 204 when it's accepted (or quietly dropped), and 400, 401, 403, 413 or 429 with a short reason when it isn't.

Privacy defaults

The write key sits in your pages, so anyone can read it, like any analytics key. The site check, the limits and the size caps are what keep it honest.

What you see

Every 10 minutes the day is rolled up into the numbers store as active_users, weekly_active_users and goal_<event>. @steward reads them in the heartbeat and the brief, and goals in the charter can name them.

Cross-promotion

When two of the studio's businesses serve the same kind of customer, @steward can propose that one mentions the other: a short block on the hosted page, or a line in its waitlist emails. It finds pairs with crosspromo_candidates (shared words in the charters' customer fields, plus tactics both have outcomes for) and proposes one with crosspromo_propose.