Go to file
Yashas e442347b6e feat: play the recording on the Live monitor too
The operator watches the Live monitor and never sees My calls, so the recording
was reachable only by the colleague who took the call. Now both audiences get it,
by the route each already uses:

  operator  -> call_recording_url on the lead, via the record view
  colleague -> GET /api/agent-sessions/:id/call, authorised by owning the session

Two paths because they are looking at two different things — a lead record and a
conversation — not because the data differs.

Placed ABOVE the summary in the drawer: the summary is a claim about the call and
this is the call, so on a demo about whether the AI captured things fairly the
evidence should not sit below the fold.

preload="none" on both. The drawer opens on every row click, and a WAV of a whole
phone call is not something to fetch speculatively.

tsc -b and vite build clean.
2026-08-27 12:31:11 +05:30
src feat: play the recording on the Live monitor too 2026-08-27 12:31:11 +05:30
.env.example feat: EV Sales Desk console — place a transferring call, watch it live 2026-08-26 22:58:25 +05:30
.gitignore feat: EV Sales Desk console — place a transferring call, watch it live 2026-08-26 22:58:25 +05:30
index.html feat: EV Sales Desk console — place a transferring call, watch it live 2026-08-26 22:58:25 +05:30
package-lock.json feat: EV Sales Desk console — place a transferring call, watch it live 2026-08-26 22:58:25 +05:30
package.json feat: EV Sales Desk console — place a transferring call, watch it live 2026-08-26 22:58:25 +05:30
postcss.config.js feat: EV Sales Desk console — place a transferring call, watch it live 2026-08-26 22:58:25 +05:30
README.md feat: rebrand to Mahindra, and ground the models in the real line-up 2026-08-26 23:36:19 +05:30
tailwind.config.js feat: EV Sales Desk console — place a transferring call, watch it live 2026-08-26 22:58:25 +05:30
tsconfig.app.json feat: EV Sales Desk console — place a transferring call, watch it live 2026-08-26 22:58:25 +05:30
tsconfig.json feat: EV Sales Desk console — place a transferring call, watch it live 2026-08-26 22:58:25 +05:30
tsconfig.node.json feat: EV Sales Desk console — place a transferring call, watch it live 2026-08-26 22:58:25 +05:30
vite.config.ts feat: EV Sales Desk console — place a transferring call, watch it live 2026-08-26 22:58:25 +05:30

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

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.