API reference

Every client — the four websites and the three Flutter apps — talks to one REST API, versioned under /api/v1. The contract is an OpenAPI 3.1 document generated from the controllers themselves.

Interactive

Swagger UI

https://api.helsen-med.com/docs — browse and try every operation. Available when the deployment enables it (SWAGGER_ENABLED, on by default).

In the repository

docs/api/openapi.json

The committed contract. CI fails if it differs from a fresh generation, so it is never stale on main.

In the repository

docs/endpoints.md

A generated table of every endpoint, who may call it and its rate limit.

How the contract is kept honest

  • The OpenAPI document is generated with pnpm --filter @mp/api gen:openapi; SDKs and the endpoint table with pnpm gen:sdk.
  • Each operation carries two extensions read from the same decorators the guards enforce: x-access (public, authenticated, or a list of roles) and x-rate-limit.
  • The TypeScript client @mp/sdk is generated from the document and used by every web surface. The Dart client (packages_dart/mp_api) is hand-written on purpose; only its error catalog and operation list are generated, and a drift test checks every path it calls exists.

Conventions every endpoint follows

TopicRule
Base path/api/v1; health at /health (liveness) and /ready (readiness, checks the database).
AuthenticationBearer JWT access token (15 minutes) plus a rotating refresh token.
MoneyInteger minor units with an ISO currency code. On the wire the amount is a string, never a JSON number.
IDsUUID v7 (time-sortable). Orders and products also have a short human reference.
IdempotencyEvery write that touches money or orders requires an Idempotency-Key header; a retry replays the stored response rather than acting twice.
PaginationCursor-based on every list endpoint, never offsets.
ErrorsEvery error carries a stable code from the catalog in packages/contracts/src/errors.ts, e.g. order.ILLEGAL_TRANSITION.
TenancyA seller only ever sees its own rows. Reading another seller's resource returns common.NOT_FOUND — never 403, which would confirm the id exists.
Rate limitA global per-IP limit (120 requests per minute unless configured), with stricter limits on sensitive routes such as sign-in and OTP.
TimestampsUTC, ISO 8601.

Endpoint groups

The generated table lists roughly 400 operations. They group by audience:

  • Public and buyer: auth, catalog, search, cart, checkout, orders, returns, reviews, questions, addresses, notifications, conversations, privacy.
  • Seller: seller-onboarding, seller-orders, seller-shipments, seller-returns, seller-earnings, seller-invoices, seller-staff, seller-performance, inventory, media.
  • Delivery: dispatch, driver, driver-onboarding, delivery-zones.
  • Staff: the admin-* groups (catalog, orders, disputes, chargebacks, commission plans, seller and driver onboarding, users, audit, feature flags, content, support) and finance.