Stratify
Internal ops

Postman And Screenshots

docs/internal/postman-and-screenshots.md

Source updated 03. Aug. 2026

Postman, OpenAPI gaps, UI walkthrough screenshots

UI walkthrough (no login)

  • Route prefix: `/walkthrough` — gated in middleware.ts (no Supabase session).
  • `WALKTHROUGH_ENABLED`: true always allows; false always denies; unset allows only when `NODE_ENV !== "production"` (so production must set explicitly).
  • Linear steps: Start → Landing → Pilot → Retail onboarding → Dashboard → Admin queue → Audit.
  • Entry from home: Team: UI walkthrough (no login) or open /walkthrough directly.

Live product routes (/dashboard, /onboarding, /pilot/onboard, /admin/*) stay Supabase-gated.

Cron targets (prod)

Scheduled routes under **/api/cron/* expect Authorization: Bearer <CRON_SECRET>. Crons run on Hetzner (Vercel cron is deactivated). In production the secret must be set or callers get 401** (fail-closed).

Screenshots — mobile (current)

Committed UI screenshots live in `docs/screenshots/` (mobile onboarding + sign-in + feed/mandates/account, each in dark and light). They are rendered as a gallery at [/handbook/screenshots](/handbook/screenshots) — the canonical view, auto-generated from the folder. No need to maintain a file list here.

These replace the old web Phase-C onboarding screenshots (KYC/IBAN/deposit/allocate), which depicted a flow that does not exist in the pre-license product.

How to regenerate (iOS simulator, no Metro)

xcrun simctl can screenshot but can't tap, and Metro hot-reload caches stale bundles — so build a Release app (JS bundled in) and drive screens by deep link:

cd apps/mobile
# 1. Release build (JS embedded — no Metro, always current code)
CI=1 pnpm expo run:ios --configuration Release --device <SIM_UDID>   # build fails only at the osascript launch step — ignore
# 2. install + launch via simctl (the run step's launch is the only failure)
APP=$(find ~/Library/Developer/Xcode/DerivedData -name Stratify.app -path '*Release-iphonesimulator*' | head -1)
xcrun simctl install <SIM_UDID> "$APP"
xcrun simctl ui <SIM_UDID> appearance dark   # or light
xcrun simctl launch <SIM_UDID> co.waitwhat.stratify
# 3. screenshot + deep-link between screens (no login needed for onboarding)
xcrun simctl io <SIM_UDID> screenshot /tmp/welcome.png
xcrun simctl openurl <SIM_UDID> "stratify://onboarding/value-prop"
xcrun simctl openurl <SIM_UDID> "stratify://onboarding/mandates"
xcrun simctl openurl <SIM_UDID> "stratify://onboarding/notifications"
xcrun simctl openurl <SIM_UDID> "stratify://sign-in"

Auth-gated screens (feed / mandates / account)

simctl can't type, so the sign-in form can't be filled directly. Instead mint a session via the Supabase password grant and inject it through the app's deep-link session handler (createSessionFromUrl). That handler is still wired up even though sign-in itself no longer uses links — see ADR-0002:

SB=https://app.stratifyinvest.com/supabase
ANON=$(grep EXPO_PUBLIC_SUPABASE_ANON_KEY apps/mobile/.env.local | cut -d= -f2-)
R=$(curl -s -X POST "$SB/auth/v1/token?grant_type=password" -H "apikey: $ANON" \
  -H 'Content-Type: application/json' \
  -d '{"email":"[email protected]","password":"<see private memory note>"}')
AT=$(echo "$R" | python3 -c 'import json,sys;print(json.load(sys.stdin)["access_token"])')
RT=$(echo "$R" | python3 -c 'import json,sys;print(json.load(sys.stdin)["refresh_token"])')
xcrun simctl openurl <SIM_UDID> "stratify://feed#access_token=${AT}&refresh_token=${RT}"
# then deep-link the tabs: stratify://feed | stratify://mandates | stratify://account

Test accounts use password auth here because simctl cannot type a 6-digit code into the sign-in form; the interactive path is the emailed code, not a password. Credentials are NOT committed here — see the private memory note stratify-mobile-auth-creds (or ask Toby). Emails: [email protected], [email protected].

Postman

Import `postman/stratify-b2b-sandbox.postman_collection.json`.

Set collection variables:

  • baseUrl — e.g. http://localhost:3000 or your preview host
  • bearerToken — Supabase-issued B2B API key in the form st_<prefix>_<secret> with scopes required by lib/auth/m2m for each route

OpenAPI source of truth: `openapi/stratify-api.yaml` (v0.2.0). Regenerate Postman: pnpm api:postmandocs/api/postman/stratify-sandbox.postman_collection.json.

Phase A — implemented (/api/v1/*)

MethodPath
GET/api/v1/mandates
GET/api/v1/mandates/{mandateId}/signals
GET/api/v1/mandates/{mandateId}/signals/{signalId}/order-intents
POST/api/v1/subscriptions
GET/api/v1/audit-log/{partnerId}
POST/api/v1/mcp (JSON-RPC)

Auth: Authorization: Bearer st_<prefix>_<secret> (Supabase-backed, not Clerk). See docs/api/quickstart.md.

Phase C — documented only (404 in Phase A)

MethodPath
GET/api/v1/pilots
GET/api/v1/pilots/{id}/strategy
POST/api/v1/mirror-orders
POST/api/v1/webhooks/execution-status

See docs/api/phase-c-endpoints.md. Do not add these to the sandbox collection until shipped.

Routes not described in OpenAPI (still exist in the app)

Use separate webhooks/cron docs or Postman folders when you need them:

AreaPaths
HealthGET /api/health
Webhooks (Phase A)POST /api/webhooks/stripe
Webhooks — Phase C (deferred, not deployed pre-license)POST /api/webhooks/ibkr, sumsub
Cron (Phase A, Hetzner, secured separately in prod)GET /api/cron/audit-archive, GET /api/cron/webhook-deliver
Cron — Phase C (deferred, not deployed pre-license)GET /api/cron/ibkr-poll

Adding these to OpenAPI is optional; keep partner contract (/api/v1/*) strict.