# Live public API verify — legacy-compatible contract **When:** 2026-08-04 21:10 +02:00 **Verdict:** **PASS** (process E2E + dual-mode OK; minor shape notes below) **API:** `http://localhost:8080` **Web:** `http://localhost:5174` **Postgres:** `localhost:5433` (`descrybe-v2-postgres`) **Auth:** Demo API key prefix `dk_demo_lo…` (redacted; full value in [demo-user.md](demo-user.md)) **Worker:** `worker.exe` running (PID observed during run) No API restart required — responses matched the current OpenAPI / handlers. ## PASS / FAIL by area | Area | Result | Notes | |------|--------|-------| | Auth (missing key) | **PASS** | `401` `{ error: { code: "unauthorized", message: "Unauthorized" } }` | | Auth (Bearer) | **PASS** | `200` products list | | Auth (X-API-Key) | **PASS** | `200` products list | | GET `/products` | **PASS** | `{ data, meta }` — presentProduct fields (`id`, `product_id`, `name`, `category`, `status`, `feed_id`, `quality_score`, `quality_grade`, `created_at`, `updated_at`) | | GET `/products/quality` | **PASS** | `{ data, meta }` with `quality_checks`; meta has `page/limit/total` (no `totalPages`) | | POST `/products/process` (items/EAN) | **PASS** | `200` `{ data: { process_id, total_items, processed_items, message } }` | | GET poll → COMPLETED + `items` | **PASS** | `full` completed in ~4s with 2 items | | Process variants | **PASS** | `normalize_only`, `title`, `category` all `200` → `COMPLETED` + `items` | | Dual-mode `raw_product_ids` | **PASS** | On same path; must be **raw** UUIDs (not processed product list `id`) | | GET categories / attributes | **PASS** | `{ data, meta }` with page/limit | | GET feeds (+ one by id) | **PASS** | List `{ data, meta }`; GET one `{ data: {…} }`; sync **not** run | | GET export-feeds | **PASS** | `{ data, meta }` | | GET campaigns | **PASS** | `{ data: { year, presets, prepared } }` (no `meta`; matches OpenAPI) | | OpenAPI process docs | **PASS** | Documents `items` / `process_id`, dual-mode, legacy vs flat `/process` | ## Process outcome (critical) ### Start (`full`, real demo GTINs) ```http POST /api/v1/products/process { "processing_type": "full", "items": [ { "ean": "8806088803005" }, { "ean": "8700000000002" } ]} ``` - **HTTP 200** - `process_id`: `d8f6d996-c0b8-43e7-9372-032fc725a523` - `total_items`: 2 ### Poll - Status **COMPLETED** on first poll (~4s) - `data.items` length **2** (Samsung dishwasher + Sample Gadget), with title/description/meta/eprel where applicable ### Variants (quick) | `processing_type` | Start | Final | `items` | |-------------------|-------|-------|---------| | `full` | 200 | COMPLETED | yes | | `normalize_only` | 200 | COMPLETED | yes | | `title` | 200 | COMPLETED | yes | | `category` | 200 | COMPLETED | yes (title may be null) | ### Dual-mode - Body `{ "processing_type": "normalize_only", "raw_product_ids": [""] }` → **200** envelope + COMPLETED with `items`. - Using a **processed** product list `id` → `400 validation_error` / `no matching products for company` (expected: IDs must be `raw_products.id`). - Flat surface `POST /api/v1/process` with same `raw_product_ids` → **202** flat `ProcessingJob` (documented separate contract). ## OpenAPI spot-check - `/api/v1/openapi.yaml` **200**, documents: - Legacy: `POST /products/process` + `items[].ean` → `200` `{ data: { process_id, … } }` - Poll: `GET /products/process/{id}` with `items` when COMPLETED - Alternate body `raw_product_ids` on the same handler - Flat `/process` surface called out as separate (not legacy envelope) ## Remaining gaps / notes 1. **ID confusion:** ~~OpenAPI under-documented~~ **Closed (docs):** OpenAPI now documents dual IDs explicitly — `GET /products` `id` = processed, `raw_product_id` = raw; `raw_product_ids` must use raw UUIDs; COMPLETED poll items expose `processed_product_id` + `raw_product_id`. Plan gates (402) + gates-before-EnsureRaw on `items[].ean` documented. 2. **Envelope inconsistency (intentional dual contract):** List endpoints use `{ data, meta }`; `GET /products/{id}` and flat `/process*` return **flat** objects (OpenAPI agrees). Not a regression vs docs. 3. **Quality meta:** `totalPages` omitted (products list includes it). 4. **Feed sync:** Skipped (avoid heavy sync spam). Single GET by UUID verified. 5. **DELETE / create:** Skipped (no throwaway unique_ids created). 6. **Export generate:** Not run (avoid large exports). ## Stack check | Service | Port | Status | |---------|------|--------| | Go API | 8080 | Up (`/healthz` ok) | | Web (Vite) | 5174 | Listening | | Postgres | 5433 | Healthy | | Worker | — | Running |