Three groups
The src/app/ directory splits into three route groups using parenthesised folder names. The parentheses are stripped from the URL, so a file at app/(public)/landing/page.tsx serves /landing.
| Group | Layout | Auth | Purpose |
|---|---|---|---|
(public) | Marketing shell, skip-link, JSON-LD | None | Landing, product pages, legal, docs. |
(authed) | App chrome (nav, profile drawer) | Reads agnt_token cookie | Consumer app and venue admin dashboard. |
api | None (route handlers) | Forwards cookie to backend | Thin proxies to the FastAPI backend. |
Public routes
Everything under src/app/(public)/. The layout injects the skip link, the Organization / WebApplication / WebSite JSON-LD, and forces dynamic = "force-dynamic" as a workaround for the Next 16 + React 19 SSG prerender bug.
/landing/for-you/for-businesses/pricing/partners/developers/stack/prompts/commerce/network/docs/manifesto/team/signals/guides/comparisons/benchmarks/glossary/changelog/case-studies/faq/blog/privacy/terms/cookies/start/p/[id]/r/[code]/u/[username]/v/[slug]/cities/[city]Authed routes
Everything under src/app/(authed)/. These routes read the agnt_token cookie and redirect to /startif it's missing. The consumer app lives under /me; the venue admin dashboard lives under /venue/admin.
/me/me/bookings/me/lists/me/lists/[id]/me/settings/notifications/upgrade/lemming/scan-demo/venue/claim/venue/admin/venue/admin/agent/venue/admin/bookings/venue/admin/customers/venue/admin/crm/venue/admin/crm/[id]/venue/admin/knowledge/venue/admin/menu/venue/admin/finance/venue/admin/accounting/venue/admin/commerce/venue/admin/analytics/venue/admin/escalations/venue/admin/settings/venue/admin/visibility/venue/admin/network/venue/admin/promote/venue/admin/clientsAPI route handlers
The PWA exposes 51 route handlers under src/app/api/. They do not implement business logic. Every handler is a thin proxy that:
- Reads the
agnt_tokencookie from the incoming request. - Forwards to the Python backend via
backendFetchorauthBackendFetch. - Preserves HTTP status codes by catching
BackendErrorand rethrowing with the right status.
import { NextRequest, NextResponse } from "next/server";
import { authBackendFetch, BackendError } from "@/lib/api";
export async function GET(req: NextRequest, { params }: { params: { slug: string } }) {
try {
const data = await authBackendFetch(req, `/api/venues/${params.slug}`);
return NextResponse.json(data);
} catch (err) {
if (err instanceof BackendError) {
return NextResponse.json({ error: err.message }, { status: err.status });
}
throw err;
}
}Layouts
Nested layouts are used for the docs shell and the venue admin chrome. For example, this page renders through:
src/app/layout.tsx(root) — fonts, global CSS, viewportsrc/app/(public)/layout.tsx— skip-link, root JSON-LD,force-dynamicsrc/app/(public)/docs/layout.tsx— marketing nav, ambient background, marketing footersrc/app/(public)/docs/frontend/routes/page.tsx— this page