Maintaining the API
Adding or changing Hono routes, keeping the OpenAPI spec in sync, implications for the API reference, and the realtime/SSE event-extension procedure.
The API layer lives in packages/api/src/routes/vtc/ (operator routes) plus Phase 2 routes for driver/, tracking/, agency/, and realtime/. The HTTP framework is Hono 4.12+ with hono-openapi for spec generation.
One platform exception matters for documentation: the shared operator-facing Better Auth entrypoint at /api/auth/* is not a generic login gateway. It intentionally rejects agency-portal users and direct driver-app logins after session creation, revokes the fresh session, and responds with 403 plus either AGENCY_PORTAL_USER or DRIVER_ACCOUNT. Any auth documentation or support guidance must keep that operator-only isolation rule explicit.
Another recovery-only exception exists for incident response: POST /api/admin/emergency-reset can recreate or repair an operator login when deploy-time credentials and the stored account are out of sync. It requires CRON_SECRET via Authorization: Bearer <CRON_SECRET> or x-cron-secret, accepts { email, password }, and upserts the user, refreshes the credential password, marks the email verified, and ensures owner membership on the first organization. Document this route as a last-resort recovery tool, not a normal login flow.
Adding a new route
1. Choose the correct bounded-context file
Route files are organized by domain:
| File | Domain |
|---|---|
pricing.ts | Pricing zones, rates, strategies |
fleet.ts | Vehicles, drivers |
crm.ts | Contacts, clients |
quotes.ts | Quotes, quote lines |
orders.ts | Orders |
missions.ts | Missions, dispatch |
billing.ts | Invoices, payments |
pricing-calculate.ts | Quote pricing calculation, RSE/staffing analysis, round-trip and multi-stop payloads |
pricing-sejour-rse.ts | STAY/sejour RSE accounting endpoint; writes nothing and only returns informational overflow data |
settings.ts | Organization settings |
integrations.ts | External integrations |
driver/ | Driver-app-specific endpoints |
tracking/ | Customer tracking endpoints |
agency/ | Agency portal endpoints |
realtime.ts | SSE stream endpoints |
2. Define the route with hono-openapi
Every new route must include hono-openapi decorator metadata so it appears in the generated openapi.json. Without it, the route is invisible to the API reference (Story 85-6).
// Example skeleton for a new route
import { describeRoute } from 'hono-openapi'
import { resolver, validator } from 'hono-openapi/zod'
app.get(
'/api/v1/missions/:id/status',
requireOperatorAuth,
describeRoute({
tags: ['missions'],
summary: 'Get mission status',
responses: {
200: {
description: 'Mission status',
content: { 'application/json': { schema: resolver(MissionStatusSchema) } },
},
},
}),
validator('param', ParamSchema),
async (c) => {
// handler
}
)3. Apply the correct auth middleware
| Audience | Middleware |
|---|---|
| Operator back-office | requireOperatorAuth |
| Agency portal | requireAgencyAuth |
| Driver app | requireDriverAuth |
| Customer tracking | validateTrackingToken (public) |
4. Run type-check and tests
pnpm --filter api exec tsc --noEmit
pnpm --filter api testFix all TypeScript errors before proceeding.
5. Regenerate the OpenAPI spec
After any route change, regenerate the OpenAPI spec artifact:
pnpm --filter @repo/api generate:openapi
pnpm --filter @repo/docs generate:api-referenceThe first command calls the in-memory /api/openapi route and writes apps/docs/public/openapi.json. The script loads .env.local first and .env second before importing the API app so Prisma and other env-dependent modules resolve correctly. The second command regenerates the FumaDocs API reference pages in apps/docs/content/docs/api-reference/. Commit both artifacts alongside the route change — do not let the spec or generated reference drift from the implementation.
Deployment-time credential repair can also matter for support docs. The ensure-admin deployment step now reapplies ADMIN_EMAIL and ADMIN_PASSWORD on every deploy, and FORCE_DB_RESET=true forces a full schema drop/reseed on the next database init. When login fails after a deploy, check the deploy secrets first before falling back to the emergency reset route.
If the OpenAPI command fails before reaching /api/openapi, check environment loading first (DATABASE_URL, auth secrets, storage credentials). The generator now depends on the same runtime wiring as the API package itself.
Cross-link: Story 85-5 describes the full spec-generation pipeline. Story 85-6 describes how the interactive API reference consumes the spec.
Changing an existing route
Backward-compatible changes (safe)
- Adding an optional request field.
- Adding a new response field (clients ignore unknown fields).
- Adding a new optional query parameter.
Breaking changes (coordinate with clients)
- Removing or renaming a request field.
- Changing a response field's type.
- Removing a route entirely.
- Changing a route's path or HTTP method.
For breaking changes, version the endpoint (/api/v2/...) or provide a deprecation window with Deprecation and Sunset response headers before removal.
Realtime / SSE — extending event types
The SSE backbone is in packages/realtime. Event types are published by API services and forwarded to connected clients.
When to add a new event type
Add a new event type when:
- A new server-side state transition must be communicated to a client in real time.
- Polling would introduce unacceptable latency for a user-facing feature.
Polling-fallback impact
Every new SSE event type must have a polling-fallback equivalent — a REST endpoint that clients can call when the SSE connection is unavailable (network interruption, service worker context, background tab). Document the polling endpoint alongside the event type.
Driver-assignment realtime now includes second-driver/relay delivery. Any mission assignment event that can involve two drivers must fan out to both driverId and secondDriverId; the driver REST mission list remains the polling fallback. Dispatch-only signals such as RSE divergence are informational and must never re-price or block the assignment response.
Procedure
- Define the new event type constant in
packages/realtime/src/events.ts(or the equivalent event catalog file). - Publish the event from the relevant service in
packages/api/src/services/. - Subscribe and forward the event in the appropriate SSE route handler in
packages/api/src/routes/vtc/realtime.ts(operator) orpackages/api/src/routes/driver/realtime.ts(driver). - Update the consuming app to handle the new event type.
- Verify the polling-fallback endpoint is in place.
Apps — when a screen change requires regenerating screenshots
Any change to a screen rendered by apps/web, apps/agency, apps/tracking, or apps/driver that alters the visible layout must trigger screenshot regeneration for the screenshot-drift gate (FR414).
Run the screenshot capture script after UI changes:
pnpm --filter docs exec playwright test --project=screenshotsCommit the new baseline screenshots alongside the code change.
Documentation impact
API changes require updating:
- The OpenAPI spec (
openapi.json) — always regenerate after a route change. - The API Reference — automatically reflects the regenerated spec.
- Any user-facing documentation page that describes the changed endpoint — use the traceability matrix (FR415).
For pricing or staffing endpoints, also update the operator quote/dispatch docs because those routes usually change visible cockpit behavior. Recent examples: POST /api/vtc/pricing/calculate now accepts sameDayRequired, returnBaseId, and staffingOverride fields; POST /api/vtc/pricing/sejour-rse-accounting powers the STAY per-day RSE badges and overflow alert.
See also: Packages overview — packages/api · Data Model maintenance · Traceability signal
Maintaining the Data Model
The Prisma schema-change procedure, backward-compatibility considerations, and when to reach for a migration versus a code change.
Traceability Signal and CI Gates
The FR→page traceability matrix, the screen registry, and the three CI gates (FR415 traceability, FR417 locale parity, FR414 screenshot drift) that protect documentation integrity.