Skip to content

HTTP Route Surfaces Map

Yaya Engine does not have one router with a uniform auth story — it has nine route files, each wired up for a different audience and, in most cases, a different guard. bootstrap/app.php mounts web.php and api.php through Laravel 12's streamlined withRouting() call, then registers mcp.php, manager.php, and (only outside production) test.php as extra groups wrapped in the web middleware stack [@bootstrap-app]. Reading the route files in isolation hides this; reading them together shows which surface a given change actually touches.

routes/api.php — three tiers under one prefix

Everything under /api splits into three trust tiers that share a prefix but nothing else:

  1. Unauthenticated PWA bootstrap endpoints, wrapped in pwa.locale middleware: magic-link send/verify, OTP send/verify, Telegram auth verification, FSP resolution, a Mixpanel proxy (so ad blockers don't intercept the frontend SDK), push-event tracking off a signed URL, and generated-link click tracking [@api-php].
  2. Sanctum-authenticated PWA user endpoints, under ['pwa.locale', 'auth:sanctum']: profile read/update, ToS acceptance, location updates, weather, active alert, loan-offering acknowledgment, user actions and pre-alert actions (list/show/activate), action-step completion and evidence upload, push-subscription management, session pinging, and the two-step manual phone-claim flow (send-code then set, both throttled tightly since a single Sanctum token could otherwise burn an unbounded SMS bill) [@api-php].
  3. Service-to-service endpoints gated by auth.admin_token: a shared bearer token, checked with a constant-time comparison in AdminTokenMiddleware, rather than any user session [@admin-token-middleware]. This tier includes the /api/v1 namespace described below, plus the WhatsApp batch endpoints and an admin-user-by-email lookup that the Rada composer uses for dev-mode phone routing [@api-php].

The /api/v1 block is the largest single piece of surface in the app: scheduled-alerts CRUD, composed-messages bulk-insert/claim, dispatch-batches, templates, users-by-town-ids, users-with-forecasts, user counts and nearest-town lookups, seasons/events (a Season → Event → Alerts hierarchy scoped to a country, allowing multiple concurrent active events per country), forecast-config and dispatch-mode per-country settings with audit trails, geography (towns/states) reads and threshold writes, town-forecasts and town-observations append-only stores, and a forecast-accuracy read for the Review Mode dashboard [@api-php]. Every block's comment cites the GitHub issue and docs/api/v1/*.md contract it implements, and the comments consistently frame this namespace as replacing direct Supabase RPC calls that alert-manager, the composer, and rada-weather used to make against a shared database — the same migration this repository's Supabase Integration and the Rada v2 Cutover page covers from the data side. The exact endpoint list, with request/response shapes, lives in api/v1 Endpoint Reference.

routes/web.php, settings.php, and auth.php — the staff-facing web app

web.php carries the WhatsApp webhook (verify/handle), the Telegram webhook, and two Inertia pages (welcome, dashboard) — the dashboard sits behind ['auth', ValidateSessionWithWorkOS::class], so a normal web-guard session still gets revalidated against the WorkOS session on every request [@web-php]. The Telegram webhook is the more defensive of the two public webhooks: VerifyTelegramWebhookSecret requires both a URL-path secret and a Telegram-sent header secret, compares both with hash_equals, and returns a 404 (not a 401) on any failure or missing configuration, specifically to hide the route's existence from anyone probing without the secret [@telegram-secret-middleware]. settings.php and auth.php round out the staff experience: settings.php is the self-service profile/appearance Inertia pages behind the same auth + WorkOS check, and auth.php is the three WorkOS AuthKit endpoints (login, authenticate, logout) that make staff SSO work at all [@web-php]. These routes are covered in depth in Auth architecture.

routes/manager.php — Yaya Manager's own guard and permission layer

Manager routes run under a different guard than the WorkOS-backed staff pages: auth:admin plus can:manager.access plus a manager.filters middleware [@manager-php]. ManagerGlobalFilters reads filter_country/filter_fsp query parameters, persists them into the session (manager_filter_country/manager_filter_fsp), and merges them back onto the request object, which is how the ops team's country/FSP scoping survives navigation across the validation queue, payments, users, and alerts pages without re-selecting it on every request [@manager-filters-middleware]. Individual actions layer on their own granular permission checks beyond the blanket manager.access gate — can:manager.validation.review, can:manager.payouts.process, can:manager.payouts.cancel, can:manager.payouts.export — so a manager user's role determines not just whether they can see a page but which buttons on it actually work [@manager-php]. See Yaya Manager Inertia Ops Console for the app this surface backs.

routes/mcp.php vs routes/ai.php — two different concerns despite the shared name

These two files are easy to conflate but do unrelated jobs. mcp.php is a small session-auth surface: login/logout for the /mcp admin web session, under the admin guard [@mcp-php]. ai.php is the actual MCP server registration file, following the laravel/mcp package's convention: it registers YayaServer at /mcp/yaya behind auth.admin_token (for Dify's machine-to-machine calls) and InternalYayaServer at /mcp/internal-yaya behind auth:api plus can:mcp.access (for the team, over Laravel MCP's built-in OAuth routes) [@ai-php]. The naming collision is a real trap for a newcomer — mcp.php is about logging a human into a web session, ai.php is about exposing tool servers to AI agents. The full tool inventory and auth story for both servers is on MCP servers and Dify AI integration.

routes/telegram.php — not a Laravel route file

Despite the routes/ location, telegram.php is the Nutgram bot's own dispatch table, not a set of Laravel Route:: calls — this is the Nutgram package's own registration convention, activated via config/nutgram.php. It wires slash commands (/start, /help, /payout) ahead of a catch-all onMessage handler, plus dedicated onContact and onLocation handlers for Telegram's Mini-App contact-share and location-share flows [@telegram-php]. Command handlers run first because Nutgram dispatches to onCommand registrations before the catch-all, so only text that didn't match a slash command reaches the freeform handler that forwards to Dify.

routes/test.php — gated out of every real environment

test.php only gets registered when app()->environment(['local', 'testing']) is true, per the conditional in bootstrap/app.php, and its routes are also excluded from CSRF validation (_test/*) [@bootstrap-app] [@test-php]. It exists purely to support deterministic E2E fixtures for the Manager Playwright suite: OTP-bypass login (/​_test/auth/token), admin-session creation, and factory-seeding endpoints for user-action-steps, pending validations, payouts, manager users/alerts/payouts, and recent-activity, plus a cleanup route that wipes the seeded tables. None of this is reachable in production, and the pattern — bypass auth, mutate the database directly through factories — would be a serious hole anywhere else; it is safe here only because of the environment gate.

Custom middleware as the connective tissue

Five custom middleware aliases carry most of the cross-cutting behavior described above: auth.admin_token (AdminTokenMiddleware, the shared-bearer-token check for service-to-service calls) [@admin-token-middleware], manager.filters (ManagerGlobalFilters, session-persisted FSP/country scoping) [@manager-filters-middleware], pwa.locale (ResolvePwaLocale, resolves and sets the request locale from the authenticated Sanctum user, an explicit X-Locale header, or Accept-Language, and flags the request as is_pwa_api so exception handlers can return PWA-shaped JSON instead of Inertia error pages) [@pwa-locale-middleware], CaptureGeneratedLinkClick (a global web middleware, not an alias, that records a ?link=slug click and drops a 30-day cookie before redirecting to the clean URL) [@generated-link-middleware], and VerifyTelegramWebhookSecret (the dual-secret, 404-on-failure webhook guard described above) [@telegram-secret-middleware]. Anyone changing PWA response shapes, service-to-service auth, or manager scoping should expect to touch one of these five files rather than a route file.