# OpenAPI

The single source of truth for the public, externally-stable API surface of
SupplierDev.

## Layout

```
openapi/
├── README.md                ← this file
└── v1/
    └── openapi.json         ← OpenAPI 3.1 spec (hand-maintained)
```

We keep the spec in **JSON** so it parses with Node's built-in `JSON.parse` and
needs no extra dependency for validation. Tools that prefer YAML can convert
on the fly (`yq`, `swagger-codegen`, etc.).

## What is in scope

Only endpoints under **`/api/v1/*`** are part of the public contract. These are
the routes a tenant or future external integration may rely on:

- `GET  /api/v1/health`
- `GET  /api/v1/master-data`
- `GET  /api/v1/suppliers`            `POST /api/v1/suppliers`
- `GET  /api/v1/projects`             `POST /api/v1/projects`
- `GET/PATCH/DELETE /api/v1/projects/{id}`
- `GET  /api/v1/oee`                  `POST /api/v1/oee`
- `GET  /api/v1/assignments`

## What is **out of scope**

These exist in the codebase but are NOT part of the v1 contract and are NOT
documented here. They may change without notice.

| Path prefix | Audience | Why excluded |
|---|---|---|
| `/api/admin/*`   | Tenant admin UI | Internal control plane for the tenant's own admin panel. |
| `/api/owner/*`   | Platform owner / SaaS operator | Multi-tenant management API; uses owner-role auth, not tenant auth. |
| `/api/demo/*`    | Demo packs UI | Tooling only. |
| `/api/planning/*`, `/api/wertstrom/*`, `/api/repository/*`, `/api/qaf-template`, `/api/projects/next-id`, `/api/holidays/seed` | First-party UI | Internal UI XHR. Migrate to `/api/v1/*` when the contract stabilizes. |

If a future external integration needs one of these, **promote it to `/api/v1/*`
first** and add it to the spec.

## Versioning

- The URL prefix carries the version (`/api/v1`).
- Breaking changes (rename, type change, remove field, change semantics) require
  a new prefix (`/api/v2/...`). v1 is supported alongside.
- Additive changes (new optional fields, new endpoints, new optional query params)
  are allowed inside v1 and **must** ship in the spec in the same PR.

## Auth / security

Two schemes documented in `components.securitySchemes`:

| Scheme | Used by | Source of truth |
|---|---|---|
| `cookieAuth` | All `/api/v1/*` routes by default | Supabase session cookie refreshed by `lib/supabase/proxy.ts`. |
| `supportSession` | Bypass for support engineers | `X-Support-Session-Id` header validated by `lib/support`. |

`security: [{ cookieAuth: [] }]` is set globally; routes that allow anonymous
access (e.g. `/health`) override with `security: []`.

## Error model

All non-2xx responses use the shared `ErrorEnvelope` schema:

```json
{ "error": "Human message", "code": "tenant_disabled", "details": { ... } }
```

`code` is the machine-readable enum from `ApiErrorCode` in `lib/api/envelope.ts`.
Optional today, **required for new routes**. The matching helpers (`unauthorized()`,
`forbidden()`, `notFound()`, `badRequest()`, `serverError()`) live in `@/lib/api`.

## Workflow: adding a new public endpoint

1. Implement the route under `app/api/v1/<resource>/route.ts`.
2. Use the envelope helpers (`@/lib/api`) for responses — never roll your own.
3. Add the path to `openapi/v1/openapi.json`:
   - tag it under one of the existing tags (or add a tag entry first)
   - reference shared `parameters`, `responses`, `securitySchemes`
   - add new schema(s) under `components.schemas` with `required` and types
4. Run `npm run check:openapi` — it validates structural shape and that every
   path is tagged + has at least one response.
5. Mention the new route in the relevant module README.

## Workflow: changing an existing endpoint

- **Additive**: update the spec in the same PR as the code change.
- **Breaking**: do not. Add a `/v2` route instead.
- If you must rename internally, the public response shape MUST stay backwards-compatible
  for v1 (additionalProperties is fine; renaming a field is not).

## Validation

- `npm run check:openapi` — schema structural check (no network).
- `npm run check:portability` — bundles `check:openapi` so CI catches drift.

## Future tooling (not enabled now)

- Type generation from spec (e.g. `openapi-typescript`) — deliberate non-goal.
  Spec lags reality only in detail; runtime types still come from the route handlers.
- Mock server — defer until a second client (iOS, BMW WebEAM bridge) actually
  consumes the spec.
