Postman, OpenAPI gaps, UI walkthrough screenshots
UI walkthrough (no login)
- Route prefix: `/walkthrough` — gated in
middleware.ts(no Supabase session). - `WALKTHROUGH_ENABLED`:
truealways allows;falsealways 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
/walkthroughdirectly.
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://accountTest 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:3000or your preview hostbearerToken— Supabase-issued B2B API key in the formst_<prefix>_<secret>with scopes required bylib/auth/m2mfor each route
OpenAPI source of truth: `openapi/stratify-api.yaml` (v0.2.0). Regenerate Postman: pnpm api:postman → docs/api/postman/stratify-sandbox.postman_collection.json.
Phase A — implemented (/api/v1/*)
| Method | Path |
|---|---|
| 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)
| Method | Path |
|---|---|
| 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:
| Area | Paths |
|---|---|
| Health | GET /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.