Widget
The widget module lets you embed a live AI chat on any website. Visitors interact through a floating chat bubble that connects to Loquent’s text agents over WebSocket. Contact info collection, origin validation, session persistence, and daily message caps are all built in.
Architecture overview
Section titled “Architecture overview”The module has four layers:
- Admin UI — Dioxus views at
/widgetsfor creating, editing, and managing widget configs - REST API — server functions for CRUD operations on widget configs
- Public endpoints — unauthenticated routes that serve the JS bundle and widget config
- WebSocket — real-time chat connection between the visitor’s browser and the text agent
Data types
Section titled “Data types”WidgetConfig
Section titled “WidgetConfig”The full widget configuration stored in the widget_config table.
pub struct WidgetConfig { pub id: Uuid, pub organization_id: Uuid, pub widget_id: String, // public identifier, e.g. "wid_..." pub text_agent_id: Uuid, pub name: Option<String>, pub greeting: Option<String>, pub primary_color: String, // default: "#6366f1" pub text_color: String, // default: "#ffffff" pub position: WidgetPosition, // bottom_right | bottom_left pub contact_info_mode: ContactInfoMode, // none | optional | required pub collect_name: bool, pub collect_email: bool, pub collect_phone: bool, pub allowed_origins: Option<Vec<String>>, pub daily_message_cap: Option<i32>, pub enabled: bool, pub created_at: String, pub updated_at: String,}WidgetPublicConfig
Section titled “WidgetPublicConfig”A safe subset returned to external sites — no org IDs or internal identifiers.
pub struct WidgetPublicConfig { pub name: Option<String>, pub greeting: Option<String>, pub primary_color: String, pub text_color: String, pub position: String, pub contact_info_mode: String, pub collect_name: bool, pub collect_email: bool, pub collect_phone: bool,}WidgetPosition
Section titled “WidgetPosition”pub enum WidgetPosition { BottomRight, // default BottomLeft,}ContactInfoMode
Section titled “ContactInfoMode”Controls whether visitors must provide contact info before chatting.
pub enum ContactInfoMode { None, // no collection Optional, // visitor may provide info Required, // visitor must provide info before chatting}WebSocket protocol
Section titled “WebSocket protocol”The widget communicates over GET /api/widget/{widget_id}/ws?token=<session_token>.
Client → Server messages
Section titled “Client → Server messages”{ "type": "message", "content": "Hello!" }{ "type": "contact_info", "name": "Jane", "email": "jane@example.com", "phone": "+1234567890" }Server → Client messages
Section titled “Server → Client messages”| Type | Fields | Purpose |
|---|---|---|
session | session_token, contact_info_submitted | Sent on connect; provides the token for reconnection |
history | messages[] (role + content) | Replays previous messages on session resume |
typing_start | — | AI is generating a response |
message | content, role | Chat message from the assistant |
typing_stop | — | AI finished generating |
contact_info_saved | — | Visitor contact info was saved |
error | message | Error description |
Public endpoints
Section titled “Public endpoints”These routes require no authentication. The widget_id acts as the lookup key.
| Method | Path | Description |
|---|---|---|
GET | /api/widget/embed.js | Serves the compiled widget JS bundle (cached 1 hour) |
GET | /api/widget/{widget_id}/config | Returns WidgetPublicConfig — validates Origin header against allowed origins |
GET | /api/widget/{widget_id}/ws | WebSocket upgrade for live chat |
Admin API (server functions)
Section titled “Admin API (server functions)”All admin endpoints require session authentication and organization-scoped access.
| Function | Description |
|---|---|
create_widget | Creates a new widget config with a generated wid_* identifier |
get_widgets | Lists all widget configs for the current organization |
get_widget | Fetches a single widget config by ID |
update_widget | Updates widget settings (validates input) |
delete_widget | Deletes a widget config |
reset_widget_id | Regenerates the public widget_id (invalidates old embeds) |
Services
Section titled “Services”| Service | Purpose |
|---|---|
generate_widget_id | Creates a unique wid_* identifier |
validate_widget_data | Validates WidgetConfigData before create/update |
validate_origin / is_origin_allowed | Checks the request Origin header against the widget’s allowed origins list |
create_widget_session | Creates a new session row for a visitor |
resume_widget_session | Resumes an existing session using the session token |
resolve_widget_contact | Matches visitor info to an existing contact or creates a stub contact |
Contact resolution
Section titled “Contact resolution”When a visitor submits contact info, resolve_widget_contact deduplicates against existing contacts:
- Look up by email (org-scoped)
- Look up by phone (org-scoped)
- If both match the same contact → use it
- If only one matches → use that contact
- If they match different contacts → use the email contact (stronger identifier)
- Neither matches → create a new stub contact
Embedding the widget
Section titled “Embedding the widget”Add this script tag to any HTML page:
<script src="https://your-loquent-instance.com/api/widget/embed.js" data-widget="wid_abc123"></script>The script fetches the widget config, renders a floating chat bubble, and opens a WebSocket connection when the visitor clicks it. Sessions persist across page navigations using the session token stored in the browser.
Admin UI routes
Section titled “Admin UI routes”| Route | View | Description |
|---|---|---|
/widgets | WidgetListView | Lists all widgets with status and actions |
/widgets/create | CreateWidgetView | Form to create a new widget |
/widgets/:id | WidgetDetailsView | Edit settings, copy embed code, reset widget ID |
File structure
Section titled “File structure”src/mods/widget/├── api/ # Server functions (CRUD + reset)├── components/ # WidgetCardComponent, WidgetFormComponent├── routes/ # widget_config_route, widget_embed_route, widget_ws_route├── services/ # Session, contact resolution, validation, ID generation├── types/ # WidgetConfig, WidgetPublicConfig, WsMessages, enums└── views/ # List, Create, Details viewswidget-js/├── src/ # Browser-side widget (chat.js, ws.js, styles.js, markdown.js)├── dist/ # Compiled widget.min.js (served via embed route)└── build.js # Build script