View Markdown
# Changes from the old server

On 2026-09-21 the old server moved to `server-old/` and this one became `server/`. Every old endpoint and where it
went. The old server can still be started (`server-old/start.sh`, port 8081) until the apps have switched.

[TOC]

## Endpoint map

| Old (`server-old/`) | New (`server/`) | Change |
|---|---|---|
| `GET /v1/health` | `GET /v2/health` | also reports the installed models and whether Gemini is on |
| `POST /v1/voice/transcribe` (`.nemo`) | `POST /v2/asr/transcribe` | one endpoint for both engines; `engine=auto/onnx/nemo` |
| `POST /v2/voice/transcribe` (ONNX) | `POST /v2/asr/transcribe` | same |
| `POST /v1/voice/parse` | `POST /v2/voice/parse` | uses the v2 NLU pipeline; the response adds `event_id` and an `asr` block |
| `POST /v2/voice/parse` | `POST /v2/voice/parse` | same URL; `language` is now required and the device id moved to the header |
| `POST /v1/nlu/parse` (form fields) | `POST /v2/nlu/parse` (JSON) | one pipeline (model → checks → Gemini → rules) |
| `POST /v2/nlu/parse` | `POST /v2/nlu/parse` | `device_id` moved from the body to `X-Device-Id` (now required) |
| `POST /v1/voice/feedback` | `POST /v2/nlu/results` | merged |
| `POST /v2/nlu/feedback` | `POST /v2/nlu/results` | merged, batched, idempotent |
| `POST /v2/nlu/sync` | `POST /v2/nlu/results` | merged |
| `GET /v1/models`, `GET /v2/models` | `GET /v2/models` | speech packs **and** the NLU model in one list, with `installed=` → `update_available` |
| `GET /v2/nlu/models`, `GET /v2/nlu/model` | `GET /v2/models?type=nlu` | same |
| `GET /v2/models/{lang}/files/{name}` | `GET /v2/models/asr-{lang}/files/{name}` | one download path for every model |
| `GET /v2/nlu/model/file` | `GET /v2/models/nlu/files/laxmi_nlu.onnx` | same |
| `GET /v2/nlu/model/vectors` | `GET /v2/models/nlu/files/vectors.jsonl` | same |
| — | `GET /v2/languages` | **new**: language picker data |
| `POST /v2/languages/requests` | `POST /v2/languages/requests` | device id moved to the header |
| `GET /v2/languages/requests/summary` | `GET /v2/admin/language-requests` | now needs `X-Admin-Key` |
| `GET /v1/nlu/training-data` | `GET /v2/admin/training-data` | now needs `X-Admin-Key`; includes what the user saved |
| `DELETE /v1/user-data?device_id=` | `DELETE /v2/user-data` | also deletes events and language requests, not only audio |
| `/`, `/mobile-docs`, `/server-guide` | `/`, `/guide/...` | Markdown docs as web pages |

That is about 24 endpoints before and 12 now.

## Rules that changed everywhere

- **App key:** every app endpoint needs `X-API-Key` (the old server had no auth).
- **Device id** is always the `X-Device-Id` header. It was a form field, a JSON field or a query parameter depending
  on the endpoint.
- **Every error** is `{"code", "message"}`, including validation errors (`invalid_request` with `details`) and 404s.
- **Admin data** (training export, language demand) needs `X-Admin-Key`.
- **The Gemini key** is sent in a header, not in the URL, so it never appears in proxy logs.
- **Nothing in `server/` reads `server-old/`:** models, code, Python environment and database are all its own.

## Data

The old database (`server-old/data/laxmi.db`) was imported into `server/data/laxmi.db` by
`scripts/import_from_server_old.py` (read-only on the old file, safe to re-run):

| old table | new table | change |
|---|---|---|
| `nlu_events` | `nlu_events` | `final_user_json` → `final_json`, `feedback_at` → `result_at`, channel `offline_sync` → `offline` (the app's id kept as `client_id`) |
| `nlu_labels` | `nlu_labels` | same columns |
| `nlu_training_logs` | `nlu_training_logs` | same columns; history only (v1 is gone, no new rows) |
| `voice_logs` | `asr_events` | `engine` from the model version; opted-in audio files copied into `data/audio/` |
| `language_requests` | `language_requests` | same columns |

## Training (nlu_model/)

`nlu_model/` keeps its defaults (`server/data/laxmi.db`, `server/models/nlu`), which now point at this server. Its
rules loader now reads `server/app/engines/rules/`. The labelling job was ported unchanged to
`scripts/label_nlu_events.py` (its 15 old tests pass here too). The dataset, labels and Colab zip data were checked
to be the same as before: see the [setup guide](/guide/setup#71-old-server-data-imported). All
training steps (Colab and PC): [NLU training guide](/guide/training).

## Not carried over

- The v1 rules-only parse path, and v1's Gemini client, which wrote lexicon files at runtime.