# Live public API E2E — legacy `items` + EAN process **When:** 2026-08-04 20:31 +02:00 **Verdict:** **PASS** **Tenant:** Local Demo Co (`ee246275-dec0-4446-9e83-58d0c16c258a`) **API:** `http://127.0.0.1:8080` **Web:** `http://localhost:5174` (IPv6 `::1`; keep using `localhost` not `127.0.0.1` for Vite) **Postgres:** `localhost:5433` (`descrybe-v2-postgres`) **Auth:** Demo API key prefix `dk_demo_lo…` (full value in [demo-user.md](demo-user.md); redacted here) ## Stack | Service | Port | Status | |---------|------|--------| | Go API | 8080 | Restarted for this run (`scripts/run-api.ps1`) so auth + legacy process binary loaded | | Web (Vite) | 5174 | Up (left running) | | Postgres | 5433 | Healthy | | Worker | — | `apps/api/bin/worker.exe` already running | ## Contract under test Legacy Descrybe public surface (not the flat `POST /api/v1/process` + `raw_product_ids` job shape): 1. `POST /api/v1/products/process` with `{ "items": [{ "ean": "…" }], "processing_type": "full" }` 2. Expect **HTTP 200** + `{ "data": { "process_id": "…", … } }` 3. `GET /api/v1/products/process/{process_id}` until `data.status == "COMPLETED"` and `data.items` is populated ## Steps & outcomes ### 0. Auth gate (no key) ```bash curl -sS -i "http://127.0.0.1:8080/api/v1/products?limit=1" ``` **Result:** `401` ```json { "error": { "code": "unauthorized", "message": "Unauthorized" } } ``` ### 1. List products (legacy envelope) ```bash curl -sS -H "Authorization: Bearer dk_demo_lo…y_v1" \ "http://127.0.0.1:8080/api/v1/products?limit=2" ``` **Result:** `200` — `{ "data": [ … ], "meta": { "page": 1, "limit": 2, "total": 4340, "totalPages": … } }` Sample rows: Samsung dishwasher (`needs_review`), Sample Gadget (`needs_review`). ### 2. Start process by EAN ```bash curl -sS -X POST "http://127.0.0.1:8080/api/v1/products/process" \ -H "Authorization: Bearer dk_demo_lo…y_v1" \ -H "Content-Type: application/json" \ -d "{\"processing_type\":\"full\",\"items\":[{\"ean\":\"8806088803005\"},{\"ean\":\"8700000000002\"}]}" ``` **Result:** `200` ```json { "data": { "process_id": "d21f718a-fda6-45f3-8d5f-a0aef7cb3c57", "message": "Processing started for 2 product(s)", "total_items": 2, "processed_items": 2 } } ``` ### 3. Poll until `items` populated ```bash curl -sS -H "Authorization: Bearer dk_demo_lo…y_v1" \ "http://127.0.0.1:8080/api/v1/products/process/d21f718a-fda6-45f3-8d5f-a0aef7cb3c57" ``` **Result:** first poll (~2s) → `COMPLETED` with **2 items**. | EAN | title | notes | |-----|-------|-------| | `8700000000002` | Sample Gadget | attrs present; `eprel: null` | | `8806088803005` | Samsung Built-in Dishwasher DW60M6040BB/EO | attrs + `eprel.energy_class=E`; images set | Envelope fields: `status`, `process_id`, `processing_type=full`, `items[]`, `total_items=2`, `processed_at`. ## Fixes applied this run | Issue | Action | |-------|--------| | Stale API binary still returned flat `{"error":"unauthorized"}` and `400 invalid json` for `items` | Stopped old `:8080` listener; restarted via `scripts/run-api.ps1` after `go build ./cmd/api` | | Compile break: duplicate / missing `ParsePageLimitOffset` while parallel legacy landings raced | Kept single alias in `pagination.go` → `ParsePageLimit`; removed duplicate definition | No secrets committed. Demo key plaintext only exists in local seed docs / DB hash. ## Blockers None for this contract. ## Related - [demo-user.md](demo-user.md) — demo credentials + API key - [api-surface-smoke.md](api-surface-smoke.md) — broader v1 smoke - [live-pipeline.md](live-pipeline.md) — native `raw_product_ids` / flat job shape