Sixième Étoile — Documentation

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:

FileDomain
pricing.tsPricing zones, rates, strategies
fleet.tsVehicles, drivers
crm.tsContacts, clients
quotes.tsQuotes, quote lines
orders.tsOrders
missions.tsMissions, dispatch
billing.tsInvoices, payments
pricing-calculate.tsQuote pricing calculation, RSE/staffing analysis, round-trip and multi-stop payloads
pricing-sejour-rse.tsSTAY/sejour RSE accounting endpoint; writes nothing and only returns informational overflow data
settings.tsOrganization settings
integrations.tsExternal integrations
driver/Driver-app-specific endpoints
tracking/Customer tracking endpoints
agency/Agency portal endpoints
realtime.tsSSE 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

AudienceMiddleware
Operator back-officerequireOperatorAuth
Agency portalrequireAgencyAuth
Driver apprequireDriverAuth
Customer trackingvalidateTrackingToken (public)

4. Run type-check and tests

pnpm --filter api exec tsc --noEmit
pnpm --filter api test

Fix 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-reference

The 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

  1. Define the new event type constant in packages/realtime/src/events.ts (or the equivalent event catalog file).
  2. Publish the event from the relevant service in packages/api/src/services/.
  3. Subscribe and forward the event in the appropriate SSE route handler in packages/api/src/routes/vtc/realtime.ts (operator) or packages/api/src/routes/driver/realtime.ts (driver).
  4. Update the consuming app to handle the new event type.
  5. 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=screenshots

Commit 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

Was this page helpful?

On this page