Routes Overview

199 route files in backend/src/routes/ (as of 2026-06, excluding co-located tests), organized by API path prefix. All routes use OpenAPIHono for automatic OpenAPI spec generation with Zod schema validation on inputs and outputs.

Route Organization

Selected mounts from backend/src/index.ts:

Path PrefixRoute File(s)Entity/Feature
/api/authauth.ts, entra-auth.tsAuthentication, Azure Entra
/api/usersusers.ts, user-groups.tsUsers
/api/buildingsbuildings/ + buildingRoles.ts, site-protocols.ts, portal-audience.tsBuildings, Building Components
/api/projectsprojects.ts, projectRoles.ts, scenarios.ts, scenario-measures.ts, project-phases.ts, document-requests.ts, funding-applications.tsProjects, Scenarios, Funding Applications
/api/measures, /api/building-components, /api/building-technologydedicated filesMeasures, Building Components
/api/contactscontacts.tsContacts
/api/companiescompanies.tsCompanies
/api/quotesquotes.tsQuotes
/api/invoicesinvoices.tsInvoices
/api/billingbilling*.ts (operations, items, accounting, templates, settings, email)Billing
/api/sevdesksevdesk.tssevDesk accounting bridge
/api/funding-programsfunding-programs.tsFunding Programs
/api/document-obtainingdocument-obtaining/ (directory)Document Obtaining
/api/files, /api/file-collections, /api/file-collection-members, /api/file-labels, /api/entity-file-links, /api/entity-collection-linksfiles.ts, files-bulk.ts, collection/link filesFiles (CAS, collection-canonical)
/api/pdf-export, /api/pdf-data-field-definitions, /api/pdf-combined-fields, /api/pdf-document-sessionspdf-*.tsPDF Templates
/api/workflowworkflow/ (directory) + workflow-package-extras.tsWorkflows
/api/flow-definitions, /api/form-submissionsdedicated filesForms
/api/portalportal/ (directory)Portal
/api/admin/*admin/ (directory: users, departments, products, finance, hubspot config, templates, …)Admin Dashboard
/api/hubspothubspot/ (directory)HubSpot Integration
/api/dashboard, /api/admin-dashboard, /api/document-obtaining-dashboarddashboard/, admin-dashboard.ts, document-obtaining-dashboard.tsDashboards
/api/engagement-tasks, /api/custom-timeline-items, /api/sync-historydedicated filesTasks, timeline, sync history
/api/configconfig/ (directory)System configuration
/api/healthhealth.ts, health/Health checks (mounted before global middleware)
/api/feedbackfeedback.tsFeedback System
/api/audit-logsauditLogs.tsAudit Logs
/api/version, /api/performanceversion.ts, performance.tsDiagnostics

There is no /api/leads route — leads sync from HubSpot into hubspot_leads and surface through HubSpot/sync routes.

Route Pattern

const route = createRoute({
  method: 'get',
  path: '/api/buildings/:id',
  request: { params: BuildingParamsSchema },
  responses: { 200: { content: { 'application/json': { schema: BuildingResponseSchema } } } },
});
 
app.openapi(route, async (c) => {
  const { buildings } = c.var.services;  // Destructure from DI
  const result = await buildings.getById(c.req.valid('param').id);
  return c.json(result);
});

Service Access Rule

Always destructure services from c.var.services — never alias:

// Correct
const { documentRequests, portal } = c.var.services;
 
// Wrong -- never alias
const requestService = c.var.services.documentRequests;

See Service Layer Pattern, Dependency Injection.

Persistence Rule (NEW code)

Routes are presentation layer and never import @/db (I#2013): persistence flows route → service → domain module. Legacy direct DB access sits on a ratcheted allowlist (scripts/check-persistence-topology.sh) and migrates on touch; quick-lint.sh flags new violations. See Persistence Topology.