Technical docs
Omni
Overview
Support reference for the current `/omni` inbox implementation.
Routes and thread loading
- Main routes:
- `/omni`
- `/omni/{conversation_id}`
- Both routes render the same page component.
- Queue loading uses:
- `GET /api/organizations/{orgId}/conversations/`
- Direct `/omni/{conversation_id}` navigation does extra fetches when that thread is not already in the loaded queue:
- `GET /api/organizations/{orgId}/conversations/{conversation_id}/`
- If that direct fetch succeeds, the frontend inserts the result as a detached thread and selects it.
- This means a direct conversation URL can open a thread that is not present in the current queue result set.
Queue scope and filters
- The backend Omni queue action currently includes:
- `api`
- `web_chat`
- `telegram`
- Queue pagination is backend-driven at `20` conversations per page.
- Footer filter badges currently map to backend `filter` query params:
- `Mine`
- `Human`
- `Waiting`
- `Escalated`
- `AI`
- The queue header also exposes channel-category UI:
- `All chats`
- `External`
- `Internal`
- The frontend still sends `channel_category`, but the current backend conversations list action ignores it.
- Result: the channel-category toggle is UX-only right now.
Queue groups shown in the sidebar
- Client-side group labels are:
- `Mine`
- `Ally`
- `Snoozed`
- `Resolved`
- `Spam`
- Grouping is frontend-only:
- AI-owned threads go to `Ally`
- human-owned threads go to `Mine`
- `resolved`, `snoozed`, and `spam` use local thread status
- `Spam` exists in the thread model, but the current Omni queue API does not expose a spam filter/state transition.
Conversation customer/title mapping
- Conversation payloads now include `participants[]` as the source of truth for membership.
- The frontend currently derives the thread's primary customer from the first active `contact_channel` participant.
- The transitional conversation-level `contact_channel` / `contact_channel_id` fields still exist for 1:1 compatibility, but group-capable conversation handling is moving toward `participants[]`.
- Conversation payloads can include:
- `title`
- `temporary_title`
- `title_source`
- Queue rows now render three visible metadata lines:
- active participant roster on the first line
- `thread.title || thread.temporaryTitle || 'Untitled chat'` on the second line
- normalized last-message preview or `No messages yet` on the third line
- For Telegram chats, private conversations keep no auto title.
- For Telegram group and supergroup conversations, the backend keeps the conversation title synced to the latest available inbound Telegram chat title.
- When Telegram topic context is present in the inbound payload, the auto title can include both the chat title and topic name.
Customer panel identity details
- The customer profile identity card now shows a copyable external source user ID when `customer.sourceUserId` is present.
- This external ID appears below the customer name in both the wide identity panel and the compact popover.
Message and transcript behavior
- Opening a thread loads:
- `GET /api/organizations/{orgId}/conversations/{conversation_id}/messages/?page=1`
- Older history loads by incrementing `page`.
- Message history pagination is `30` per page.
- Omni skips rendering backend messages when:
- `status === 'in_progress'`
- content is empty and there are no attachments
- System messages stay in the transcript.
- Store logic excludes system messages from last-message preview recalculation.
- For Telegram conversations, backend history and frontend live insertion both deduplicate retried/redelivered inbound messages using `tg_message_hash`.
- When duplicate Telegram hashes exist in stored history, the backend prefers the row whose `employee_id` matches the conversation's `ai_employee_id`.
- The Omni message DTO now includes:
- `contact_channel_id` for contact-role messages
- `tg_message_hash` for Telegram-backed messages
- Backend sender resolution for contact-role messages is message-specific, not only conversation-specific:
- full contact name when available
- otherwise `auto_name`
- otherwise linked user Google name
- otherwise contact email
- fallback `Contact`
- Outgoing message bubbles show read-only feedback state when reaction fields are present:
- `Liked`
- `Disliked`
- `Comment added` for disliked messages with `feedbackComment`
- Omni send is still text-only:
- the footer passes `enableAttachments={false}`
- attached files are not sent from this page
- In the Omni composer, `Enter` sends the message.
- `Shift+Enter` and `Alt+Enter` insert a newline without sending.
Takeover, handoff, and local status actions
- Manual takeover uses:
- `POST /api/organizations/{orgId}/conversations/{conversation_id}/assign/`
- `Back to AI` uses:
- `POST /api/organizations/{orgId}/conversations/{conversation_id}/unassign/`
- The footer notice while AI owns the thread is:
- `AI is handling this thread.` with action `Take over`
- The header action for human-owned threads is:
- `Back to AI`
- Resolve and snooze are currently local store transitions in Omni.
- Wake-from-snooze is also local store behavior unless a real backend conversation event replaces it.
- The frontend does not call a resolve/snooze/spam backend conversation route from this page.
- The backend still exposes automation control:
- `PATCH /api/organizations/{orgId}/conversations/{conversation_id}/automation/`
- The current Omni UI uses assign/unassign, not the automation patch route, for human/AI ownership switches.
WebSocket events Omni consumes
- `conversation.message_sent`
- `conversation.message_updated`
- `conversation.message_deleted`
- `conversation.updated`
- `contact.updated`
- Queue counts are read from `payload.meta.counts` when present.
Shortcut drift worth knowing
- The help dialog currently lists:
- `F1`
- `Ctrl/Cmd + 1-9`
- `Ctrl/Cmd + Up/Down`
- `Ctrl/Cmd + T`
- `Ctrl/Cmd + H`
- `Ctrl/Cmd + Shift + R`
- `Ctrl/Cmd + Shift + S`
- `Esc`
- `Ctrl/Cmd + Enter`
- The dedicated Omni keyboard hook currently wires:
- `F1`
- `Ctrl/Cmd + 1-9`
- `Ctrl/Cmd + Up/Down`
- `Ctrl/Cmd + H`
- `Ctrl/Cmd + Shift + R`
- `Ctrl/Cmd + Shift + S`
- `Esc`
- `Ctrl/Cmd + T` is listed but not implemented in the Omni shortcut hook.