Dialpad Tech Console
A personal call companion for support engineers. Three states: idle, on call, and wrap-up. Surfaces AI-generated call scripts, live floor health, per-call CSAT, and an end-of-shift call log with AI recaps and note review.
What This Tool Is
The tech console is the personal call companion for each engineer on shift. It is intentionally narrow in scope: show the tech their own data, guide them through the current call, and let them review their shift at the end of the day. It does not expose the live queue, other techs' stats, or any administrative controls.
Get live floor health at a glance
Follow AI-surfaced call scripts during calls
Record wrap-up notes and disposition
Review their own call history at end of shift
Read AI recaps for past calls
Edit notes after a call is closed
View other techs' stats or call logs
Edit call scripts or KB steps
Publish to SharePoint
Change call routing or Dialpad settings
Access the manager dashboard
Select or pick calls from a queue
Architecture
The entire left panel is a state machine with three mutually exclusive states. Only one is visible at a time. Transitions are driven by WebSocket call events in live mode, or by the sim bar buttons in demo mode.
connected WebSocket event → oncall. hangup event → wrapup. Save wrap-up → idle. The sim buttons at the bottom of the left panel simulate these transitions in demo mode.GET /users/me to resolve identity, GET /calls for call log, POST /stats for shift metrics, GET /callcenters/{id}/status for floor health.Dialpad WebSocket — Real-time call events drive the state machine. Connected to
wss://platform-websockets-6kqb5ajsla-uc.a.run.app/events/{token}. Reconnects every 55 minutes before Dialpad's 60-minute disconnect.SharePoint REST API —
scripts.json loaded read-only at startup. Tech console never writes to SharePoint.sessionStorage — Per-call notes, dispositions, and AI recap cache stored locally via
_callNoteStore. Survives page reload within a shift. Cleared when browser closes.
UI Panels
#ktc-bardashboard-dialpad-console.html.My Console link — active page, green style.
Tech selector dropdown —
#tech-sel — In demo mode allows switching between simulated techs to test the console. In live mode this dropdown is informational only — the tech identity is resolved from GET /api/v2/users/me using the API key. Known Limitation The selector is not hidden in live mode. It does not affect live data queries, which always use the resolved user ID. A future update will hide or disable the selector once a key is saved.API key input + SET button — Saves key to
localStorage, sets MODE='live', triggers resolveUser() then data load and WebSocket connect.Mode dot — Yellow (DEMO) or green (LIVE).
Live clock — Updated every second.
#floor-barA single 40px horizontal strip. The tech's only view of the floor. Three health states drive the entire bar's background color and message.
| State | CSS class | Dot | Message pattern | Trigger |
|---|---|---|---|---|
| Nominal | .fl-ok | Green | "Queue clear — focus on quality." | Pressure <1 AND SLA ≥80% |
| Elevated | .fl-warn | Orange | "Queue building. Stay efficient." | Pressure ≥1 OR SLA <80% |
| Critical | .fl-crit | Red (pulsing) | "Floor under pressure. End calls fast." | Pressure ≥2 OR SLA <70% |
Four floor metrics shown as numbers (Queue, Free, SL%, Abn%) plus four personal stats on the right strip (Calls, Avg Handle, FCR, Abandoned). Color-coded by threshold. Updates every 30 seconds via liveTick() calling GET /api/v2/callcenters/{id}/status.
#st-idleGET /api/v2/users/me in live mode; from #tech-sel in demo mode.4 stat cards — Calls Today, First-Call Resolution %, Avg Handle Time, Abandoned on Me. Each card has a color-coded top border (green good / yellow watch / orange warn) and a sub-label (target or status).
Call log — Every call this shift as an expandable row. Click any row to expand it. See the Call Log section for full detail.
#st-oncallTimer block — Large duration counter. Green → yellow at 5 minutes (>300s) → red blinking at 8 minutes (>480s). Progress bar below scales from green to red at 10 minutes. In live mode, timer starts from
date_connected timestamp in the WebSocket event (catches any elapsed time if the event arrives late).Caller info — Caller number (
external_number from call event), queue name (group_id), issue type, contextual tags (PRIOR TICKET, VIP).Runbook checklist — Steps from
scripts.json for the matched issue type, rendered as checkable items. Click a step to check it off. Steps marked as done show strikethrough. Checks are session-only — not persisted. The checklist is for cognitive support during the call, not for tracking compliance.
#st-wrapupNotes textarea — Pre-filled placeholder names the issue type from the call. Tech types their notes here.
Save & Return button — Calls
saveWrapup(). Stores notes immediately to sessionStorage then calls PUT /api/v2/calls/{id}/labels and PATCH dutystatus to set tech back to available in all their call centers.Skip — Returns to idle without saving. Notes are lost. Disposition is not written to Dialpad.
Clear — Resets disposition selection and clears notes textarea without leaving wrap-up state.
#kb-panelIdle: Shows a "Scripts load when a call connects" placeholder.
On-call / wrap-up: Shows script cards for the active issue type. Content depends on the
source field in scripts.json:• custom — renders your runbook cards
• dialpad — shows a purple notice to follow the Dialpad AI Playbook in the app
• both — Dialpad notice at the top, then your custom cards below
Fallback: If
scripts.json is not loaded or the issue type has no match, falls back to the hardcoded KB object in the console JS, then to KB_DEFAULT (identity verification, documentation, escalation rule).
#sent-panelLive source:
pcsat_score field in the Dialpad WebSocket hangup event payload. Dialpad returns this on a 0–10 scale. The console normalizes to 0–5 for display.Demo mode: Scores are randomly generated in
genState().Source label below the chart reads demo in demo mode, pcsat_score · live in live mode.
Color thresholds: ≥4/5 green · ≥3/5 yellow · <3/5 red.
#ev-panelFour severity levels: 🔴 Critical (red left border), 🟠 Warning (orange), 🟡 Info (yellow), 🟢 OK (green).
Events are generated by
buildEvents(floor, my) every time floor data updates. They are not fetched from an API — they are derived locally from the same floor metrics that feed the ambient bar.Examples of translated language: instead of "SLA breach — 67%", the event reads: "SLA breach — escalate anything you can't resolve in 8 min."
Critical count badge shown in the panel header. If >0, the panel header border turns red.
#idle-logClick to expand — Reveals a full detail panel below the row. Expand is handled by event delegation on
#idle-log using data-key attributes (no inline onclick). Multiple rows can be expanded simultaneously.Detail panel contents:
• Caller, queue, duration (color-coded), disposition
• AI Predicted CSAT (from
pcsat_score, if available)• Notes block — what the tech typed during wrap-up
• Edit Notes button — turns notes block into an editable textarea inline
• AI Recap section — Dialpad's AI-generated call summary
AI Recap behavior: In live mode, fires
GET /api/v2/calls/{id}/ai_recap on first expand only (lazy load). Result cached in _callNoteStore. Never re-fetches. In demo mode, uses pre-written realistic recaps from the DEMO_RECAPS template bank, keyed by issue type.Edit Notes: Saves to
sessionStorage immediately, then calls PUT /api/v2/calls/{id}/labels in live mode.
API Reference
All live API calls go through a proxy at PROXY_BASE. Auth header Bearer {API_KEY} is set on every call by apiFetch(). The proxy forwards to https://dialpad.com/api/v2 with the stored credentials.
| Endpoint | Method | Purpose | Response Used |
|---|---|---|---|
/dialpad/users/me | GET | Resolve tech identity from the API key. Fired immediately on SET. | id → stored as MY_USER_ID. name → updates tech name display. |
| Endpoint | Method | Purpose | Notes |
|---|---|---|---|
/dialpad/stats | POST | Initiate today's call stats for this user | Body: {stat_type: 'calls', is_today: true, target_type: 'user', target_id: MY_USER_ID, timezone} |
/dialpad/stats/{id} | GET | Retrieve CSV result. Called 20 seconds after POST (Dialpad minimum). | Fields used: answered, abandoned, duration_avg_seconds. Cached 30 min by Dialpad. |
| Endpoint | Method | Purpose | Notes |
|---|---|---|---|
/dialpad/calls?target_id={MY_USER_ID}&limit=20 | GET | Load recent call history for the idle state log | Response: items[]. Fields: external_number, direction, date_started, duration (ms), group_id, disposition. |
/dialpad/calls/{id}/ai_recap | GET | Fetch AI-generated recap for a specific call. Lazy-loaded on first row expand. | Response fields: summary, transcript_summary, text. First non-null used. Cached in _callNoteStore. |
/dialpad/calls/{id}/labels | PUT | Save wrap-up disposition and notes to the call record | Body: {label: 'Resolved', notes: '...'}. Called on wrap-up Save and Edit Notes Save. |
| Endpoint | Method | Purpose | Notes |
|---|---|---|---|
/dialpad/callcenters/{id}/status | GET | Real-time floor metrics. Polled every 30 seconds for all of the tech's call centers (MY_CC_IDS). | Fields: agents_available, queue_depth, service_level, abandon_rate, agents_on_call, agents_in_wrapup, agents_total. Aggregated across all CCs. |
| Endpoint | Method | Purpose | Notes |
|---|---|---|---|
/dialpad/callcenters/{cc_id}/operators/{user_id}/dutystatus | PATCH | Set tech back to 'available' in all their call centers after wrap-up Save | Body: {on_duty_status: 'available'}. Called once per CC in MY_CC_IDS after saveCallLabel() resolves. |
wss://platform-websockets-6kqb5ajsla-uc.a.run.app/events/{token}Token obtained by calling
POST /api/v2/websockets. Token expires after 60 minutes. Console reconnects at 55 minutes to stay ahead of Dialpad's forced disconnect.
| Event Type | State Trigger | Key Fields Used |
|---|---|---|
Call event state='connected' | Flip to oncall state | external_number (caller), group_id (queue), date_connected (compute elapsed time), call_id, target.id (for identity check) |
Call event state='hangup' | Flip to wrapup state | duration (ms → divide by 1000), pcsat_score (normalize 0–10 to 0–5), csat_score fallback |
Call event state='queued' | Increment floor queue count | Used to update ambient bar in real time without a full status poll |
Call event state='abandoned' | Increment abandon counter | Adjusts floor abn value locally |
| Agent status event | Update MY_CC_IDS, trigger floor status poll | on_duty_status, call_center_ids[], target.id |
target.id === MY_USER_ID. Events for other techs' calls are used only to update floor pressure (queue count, abandon rate) — never surfaced to the tech directly._callNoteStore[callKey].aiRecap in sessionStorage.Loading state: the AI Recap section shows "Loading AI recap…" with a blink animation while the request is in flight.
Response fields checked in order:
summary, transcript_summary, text. First non-null value used.If no real call ID available (demo mode, or call connected before user resolved): shows "AI recap not available — call ID not resolved."
Config Fields
| Constant | Default | Description |
|---|---|---|
PROXY_BASE | 'http://localhost:3001' | Base URL of the proxy server. Change to deployed URL before going live. Used as the base for all apiFetch() calls. |
API_KEY | From localStorage | Dialpad API key. Base64-encoded in storage under dp_api_key. Auto-restored on page load. Set via the KTC bar input. |
MY_USER_ID | null | Resolved from GET /users/me on key save. Scopes all stats, call log, and duty status calls to this user. |
MY_CC_IDS | [] | Call center IDs resolved from WebSocket agent status events. Used for floor status polling and duty status updates. |
| Constant | Default | Description |
|---|---|---|
SP_SITE | '' | SharePoint site path. Must match exactly the value in the manager console. Set together or scripts will load from different paths. |
SP_DATA_FOLDER | '{SP_SITE}/pages/config/current' | Folder path. Scripts.json is loaded from here on DOMContentLoaded via loadScriptsJson(). |
scripts.json on every page load. It never caches between sessions. If a manager publishes new scripts, the tech gets them the next time they load the console — no manual refresh needed._callNoteStoredp_call_notescallId (or demo-{n} for demo calls). Each entry:notes — wrap-up notes textdisp — disposition labeldur — duration in secondscaller — external numberqueue — queue namepcsat — raw Dialpad CSAT score (0–10)aiRecap — cached AI recap text (null until first expand)callId — real Dialpad call ID (null in demo)Persistence: Survives page reload within the same browser tab. Cleared automatically when the browser or tab closes. Never persists between shifts.
Proxy Activation Checklist
Complete in order. The console runs in demo mode until all steps are done. Sim buttons and all panels work in demo mode for training and testing.
/dialpad/* route structure. If the manager console proxy is already deployed, point PROXY_BASE to the same URL.var PROXY_BASE = 'http://localhost:3001' to your deployed proxy URL. Do not add a trailing slash.SP_SITE to match the manager console. If SharePoint is not configured, the console falls back to the hardcoded KB object — the console still works, techs just see the default scripts instead of manager-maintained ones.GET /users/me, loads their call log and stats, and connects the WebSocket. Mode dot turns green.Limitations
MY_USER_ID from users/me. However it may be confusing to techs. A future update will replace it with a plain-text name display once a key is set._queue_map in scripts.json maps queue names to issue types (e.g. "Tech" queue → "Network Issue" scripts), but this requires the queue name to match exactly. If no match is found, the KB panel shows the default verification/documentation scripts.POST /api/v2/websockets to get a new token. During the ~1 second reconnect window, call events may be missed. This is unlikely to cause issues in practice but is a known gap.sessionStorage are cleared when the browser closes. Techs who close their browser mid-shift lose any unsaved local context. Wrap-up notes that were saved via PUT /calls/{id}/labels are preserved in Dialpad. Notes edited after the fact via the call log Edit button are re-saved to Dialpad in live mode, but only persist locally in demo mode.Troubleshooting
MY_USER_ID not resolved so the event is filtered out.Fix: Confirm the API key was entered and SET was clicked. Check browser console for WebSocket connection errors. Confirm
PROXY_BASE is correct and proxy is reachable. Check that the call event's target.id matches the resolved MY_USER_ID.SP_SITE is not configured (SP not set up), or the queue name in the call event does not match any key in _queue_map in scripts.json.Fix: Confirm
SP_SITE is set and matches the manager console. Check the browser console for the scripts.json loaded message. If loaded, verify _queue_map contains an entry for the queue name coming in on calls.MY_CC_IDS is empty — the call centers were not captured from WebSocket agent status events.Fix: Confirm at least one agent status event has been received (the tech must have gone on-duty or had a status change since page load). The
call_center_ids field in the agent status event populates MY_CC_IDS. Without it, the PATCH dutystatus call has no CC IDs to iterate over.data-key attributes and event delegation — no inline onclick. If expansion still fails, check browser console for JS errors on page load.DEMO_RECAPS.Cause 2: Live mode with the call ID not resolving from the WebSocket event. Check that
call_id is present in the WebSocket hangup event and being stored in _currentCall.callId.Cause 3: AI recap not available for this call in Dialpad (not all calls generate recaps, depends on plan and call duration).
loadFloorStatus() is being called (requires proxy + MY_CC_IDS populated). Also confirm the 30-second poll interval is running by checking for the liveTick() setInterval in browser debugger.