# Mahindra EV Sales Desk Console Two-screen operator console for the **live call-transfer demo**: place an outbound Mahindra EV sales call, and watch it transfer to a human colleague mid-call. Backs onto Zino app **583** (org 900000104, `dev`). Line-up: **BE 6**, **XEV 9e**, **XEV 9S**. Built on the frontgen React scaffold — Vite + Tailwind + React Router + TanStack Query + `src/zino-sdk`. ## The two screens **Place a call** — lead name, model, and the two numbers: the **customer** Meera dials, and the **colleague** the transfer bridges to. Submitting starts a `Lead Outreach` workflow instance; its trigger places the call server-side. **Live monitor** — polls the leads record view every 3s. `Transferred to human` is the transfer having fired. ## Running locally ```bash npm install echo 'VITE_ZINO_API_URL=https://dev-apps.getzino.in' >> .env npm run dev ``` Sign in with an app user of app 583 (e.g. `salesops@mahindra-ev.demo` / `Mahindra@2026` (prefilled on the login screen)). `VITE_ZINO_API_URL` is only read from `.env` in **dev**. A production build requires the `config.js` the server writes at placement time, and throws if it is absent — a promoted artifact must never fall back to the build machine's value and quietly call the wrong backend. ## API notes worth keeping The base URL is the **root** (`https://dev-apps.getzino.in`), not an app-scoped path, because login and the app APIs live at different depths: | Call | Path | |---|---| | Login | `POST /usr/login` | | Start a lead | `POST /app/583/start` | | Leads list | `POST /app/583/view/recordview` | **`src/api.ts` bypasses two SDK methods on purpose** — both are SDK bugs, not preferences: - `workflows.startWorkflow()` posts `workflow_id`, but core's `StartRequest` reads **`workflow_uuid`** and rejects the call outright. - `views.getTabularView()` issues `GET /view/recordview` — the legacy unscoped route. The app-scoped one is **POST** `/app/{id}/view/recordview` with the paging/filters in the body. Auth still goes through the SDK, so there is one token and one session. ## Why the browser holds no API key Screen 1 submits a workflow activity as the signed-in user. The workflow's own trigger then places the call using the agent key held in its server-side config. The `zvk_` key never reaches the browser. ## The per-call transfer number `handover_number` rides on the lead into the call's `context`, and agents-backend prefers it over anything configured on the agent — the agent's numbers are the fallback for calls that don't name one. Requires an agents-backend carrying the per-call override; without it the field is sent, silently ignored, and the call transfers to the agent's default number instead. ## Known gap The live transcript and the co-pilot's suggestions live in **agents-backend**, which the app-user token does not reach — the SDK talks to the renderer's workflow/view services. So the monitor shows call *status* live, but not the turn-by-turn feed. Closing that needs either a new endpoint or the trigger writing transcript lines onto the instance.