Go to file
Yashas 4b590474e4 console: the call, in the customer's own words
Recording is now on for Meera (93) and the transcript is kept on the lead
(94), so the call stops being a thing only one AI ever saw.

The wake that ends a call already carried far more than Engage used — a
summary, a sentiment analysis, and the FULL TRANSCRIPT, turn by turn —
and all of it was dropped once the three-sentence write-up was done. So a
customer said "I think it's 12,000 rupees" and "lower premium", and the
file held a paraphrase.

That matters here more than it would elsewhere: consent_artefact =
verbal_call is written from what the agent HEARD, and it is the artefact
justifying every later contact. The evidence for it should be the
customer's own words.

"Hear the call" now sits on the Log Contact entry and opens the
conversation, laid out like the WhatsApp thread — agent right, caller left
— because it is the same kind of thing and should not need learning
twice. It also files under Contact in the lead record.

Turns are split on the speaker labels rather than on newlines: a single
turn wraps, and splitting per line shattered one sentence into four
bubbles. A transcript that will not parse is shown raw — it is still
evidence, and "nothing to display" would be a lie.

NOT INCLUDED: the audio. Recording is enabled and the WAV is uploaded,
but recording_url is delivered to the agent's post-call WEBHOOK
(voice_internal_handler.go:429), not in the wake payload, so nothing puts
it on the lead. Wiring that webhook to an external-API activity is a
separate change, worth doing when someone asks to listen rather than to
read.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 17:19:53 +05:30
public/brand Wire the console to the platform and make it deployable 2026-08-24 15:20:13 +05:30
src console: the call, in the customer's own words 2026-09-08 17:19:53 +05:30
.env.example Wire the console to the platform and make it deployable 2026-08-24 15:20:13 +05:30
.gitignore Updated to new frontend specifications 2026-09-04 13:26:20 +05:30
CLAUDE.md Updated to new frontend specifications 2026-09-04 13:26:20 +05:30
eslint.config.js Empty vite-react project 2026-08-24 12:27:21 +05:30
index.html fix(build): add BASE_HREF placeholder + config.js tag to index.html 2026-08-24 15:47:26 +05:30
package-lock.json Wire the console to the platform and make it deployable 2026-08-24 15:20:13 +05:30
package.json Wire the console to the platform and make it deployable 2026-08-24 15:20:13 +05:30
README.md Wire the console to the platform and make it deployable 2026-08-24 15:20:13 +05:30
vite.config.js Wire the console to the platform and make it deployable 2026-08-24 15:20:13 +05:30

Zurich Kotak — Lead Desk

Operator console for the Zurich Kotak "Lead to Policy" demo. Leads arrive through three channels — direct, bancassurance and agency — and run one state machine to policy issuance. The agents do the work; a person appears at one queue.

Workflow config is seeded from sm2/custom-apps/zurich-kotak/ (org 84, app 536, workflow zk_wf_lead). Design doc: sm2/app-designs/zurich-kotak/design.html. Workflow spec PDF: sm2/custom-apps/zurich-kotak/zurich-kotak-workflow.pdf.

Run it

npm install
npm run dev          # http://localhost:5175
npm run build        # → dist/

.env carries the API host for local dev only:

VITE_ZINO_API_URL=https://dev.getzino.in

Logins (all password ZurichKotak@2026, org 84):

Email Role Can do
sanjay.uw@zurichkotak.example SME Underwriter Clear / Decline Referral — the only human decision
meera.rm@zurichkotak.example Bancassurance RM Submit a Partner Bank Lead
arjun.posp@zurichkotak.example POSP / Broker Submit a Partner Agent Lead
kavya.csr@zurichkotak.example Direct / Call centre Submit a Self-Serve Lead
deepa.ops@zurichkotak.example Operations Cross-channel visibility

Sign in as Sanjay to land in Referred to Underwriting, which is where the demo happens.

How it talks to the platform

Everything goes through src/api/client.js:

  • POST /usr/login — auth. org_id must be a string; a number returns cannot unmarshal number into Go struct field LoginRequest.org_id.
  • POST /app/536/view/recordview — every queue is one record view (zk-rv-leads) filtered server-side on current_state_name.
  • POST /app/536/view/form-screens then POST /app/536/activity — forms are read from the live activity schema and submitted straight back.

Forms are not defined in this repo

Whatever /view/form-screens returns is what renders — labels, types, select options, which fields are mandatory. Add a field to an activity in Studio, redeploy, and it appears here with no frontend change. That is deliberate: the workflow is the source of truth, and a hardcoded form would quietly diverge from it.

Permissions are never enforced here

The console does not hide a button to stop someone using it. Every permission decision is the workflow's, made server-side on each submission — the platform refuses and this app reports what it said. Two refusals worth knowing: /activity answers 403, /start answers 400, and both carry permission denied.

The runtime-config contract

VITE_ZINO_API_URL is read at runtime from a config.js the server writes when it places the build — never compiled in. One artifact is promoted between environments unchanged, so a build-time URL would point every environment at whichever backend happened to build it. requireConfigValue throws if it is missing, so a production build with no config.js fails loudly rather than calling the wrong backend.

vite.config.js uses base: './' and the router takes its basename from the <base href> the server writes, so one build serves any mount path. Do not reintroduce a build-time base.

This is also why the fonts and logo live under src/assets/ and not public/. Vite rewrites bundled asset URLs to be relative; a public/ file referenced as /fonts/… stays absolute and 404s under the /zurich-kotak/ mount path. The favicon is the one exception — it stays in public/brand/ and is referenced relatively from index.html.

Deployment

Registered with frontgen by sm2/custom-apps/zurich-kotak/05_frontgen_project.sql (slug zurich-kotak, repo_name zurich_kotak, org 84). A push to main builds and serves at https://preview-dev.getzino.in/zurich-kotak/.

The webhook only fires on a new push — a push made before the frontgen project row existed matched no project and did nothing.

State of the build

Piece Status
Brand — Zurich Sans, palette, logo done (kept from the original scaffold)
Sign-in against the real gateway done
Runtime config, relative base, router done
Pipeline sidebar (mirrors tbl_wf_states) done
Queue lists waiting on the zk-rv-leads record view
Lead file, activity forms, attribution panel waiting on zk-dv-lead + form screens