Voice First
Vocali prioritizes real-time, low-latency audio processing above all else. Speed is the difference between a tool and an experience.
Loading
This page documents the engineering decisions, performance discoveries, and architectural invariants of the Vocali platform. By maintaining this record, we ensure future development aligns with the core principles of low-latency AI communication.
Vocali prioritizes real-time, low-latency audio processing above all else. Speed is the difference between a tool and an experience.
AI shouldn't trap users. The architecture guarantees a smooth transition to a human agent the moment intent shifts or frustration is detected.
Built from day one to support B2B SaaS requirements: RBAC, isolated tenant data in Convex, and strict auth boundaries.
The widget does not need a build-time Vapi key. It loads the organization’s public key only after the organization, session, and widget settings have been validated.
The Convex Agent runtime persists prompts, assistant output, and tool results in the order expected by the model provider. Conversation state remains durable and reactive in Convex.
Clerk protects operator routes and organization membership, while the public widget uses a short-lived contact session. Both identities must be checked at their own boundary.
Jotai holds widget-local state such as the active screen and organization-scoped browser session, while CSS handles decorative animation and responsive presentation.
Widget startup has ordered dependencies: organization, existing session, settings, then optional voice credentials. Each asynchronous step must ignore work after unmount or a changed input.
Knowledge-base entries are inserted into an organization namespace, carry organization metadata, and are checked again on deletion.
Provider credentials are encrypted with AES-256-GCM in backend storage. The public widget endpoint returns only the public Vapi credential it needs to connect.
The proxy distinguishes public widget-support pages from protected dashboard routes and redirects signed-in users without an active organization to selection.
apps/web/modules/auth/ui/layouts/auth-layout.tsxContains the branded, hydration-safe auth presentation and its visual transition boundary.
packages/ui/src/components/button.tsxCentralized button design system with numerous light-mode optimized variants and gradients.
packages/backend/convex/schema.tsDefines conversations, sessions, organization settings, subscriptions, and encrypted provider records.
apps/widget/modules/widget/ui/screens/widget-loading-screen.tsxCoordinates organization validation, session restoration, widget configuration, and optional voice setup.
packages/backend/convex/public/messages.tsEnforces contact-session ownership before visitor messages are read or created.
Check the initialization step in order: organization ID, stored session scope, widget settings, then Vapi credentials. A missing optional voice integration should still route the visitor to chat rather than fail the entire widget.
Use Convex’s reactive/paginated queries for durable lists and keep animation work in CSS. Avoid a page-wide polling loop for conversations, files, or widget state.
The dashboard, widget, embed bundle, and Convex deployment are separate release surfaces. Set the production widget URL at embed build time and configure Clerk webhooks against the Convex HTTP endpoint.
Anonymous contact-session creation is intentionally public for the embed flow. Add rate limiting, abuse controls, and end-to-end ownership tests before operating it at high-volume public scale.
Clerk organization membership protects operator workflows; contact sessions protect visitor workflows.
A public message operation must prove the contact session owns the conversation and thread it targets.
Knowledge-base retrieval and file ownership always stay inside an organization namespace.
The Convex Agent runtime owns persisted tool-call ordering.
Only public provider credentials may cross the widget boundary.
Widget initialization advances through explicit, cancellation-safe states.
Route access is classified in the proxy before the dashboard renders.
CSS handles presentation; local React/Jotai state handles interaction and request lifecycle.