D Diagent docs

Run your workspace

Inbox & human takeover

The Inbox is your lead-and-takeover console. /app/inbox lists every captured lead; opening one shows the full conversation transcript that produced it and lets you jump in as a human operator to continue the thread.

Layout

/app/inbox shows leads on the left, sorted by recency. Click one and the end-hand pane shows the conversation that produced it: visitor messages on one side, agent replies on the other, human-agent messages from past takeovers in their own role (user / assistant / human-agent).

The conversations index at /app/conversations covers every conversation regardless of whether it produced a lead โ€” useful for spelunking past sessions that didn't convert. From there you can open a conversation and use the same takeover controls.

Live updates

Every open conversation subscribes to its private Reverb channel (conversation.{id}) for real-time updates. New messages appear without polling; the takeover state propagates to both the visitor's widget and any other operator looking at the same thread.

Taking over

Click Take over. A few things happen:

  1. The conversation gets claimed_by_user_id + claimed_at set to you (route: POST /app/conversations/{conversation}/claim).
  2. A conversation.claimed Reverb event fires on the conversation's private channel โ€” the visitor's widget shows "Human is here" in the chat header.
  3. The AI is paused. Every visitor message routes to the inbox; every reply you type streams to the visitor as a human-agent role message via POST /app/conversations/{conversation}/reply.

Releasing

Click Hand back to bot to release (route: POST /app/conversations/{conversation}/release). The next visitor message goes through the RAG pipeline again. The visitor's chat header flips back to the agent's persona. Useful when:

  • You answered the off-script question and the rest is back to FAQ territory.
  • The visitor is satisfied and likely to leave.
  • You're ending your shift โ€” handing back keeps coverage 24/7.

Capturing leads

Leads come in two ways:

  • Visitor-driven โ€” the widget's inline lead form, fired by a behavior rule or by the visitor explicitly asking to be contacted. Submitted leads land in /app/inbox.
  • Webhook-driven โ€” every captured lead fires the lead.captured outgoing webhook so you can fan it into your CRM. See Outgoing webhooks.

Live in-app toasts

The admin shell polls GET /app/leads/feed every 30 seconds for newly captured leads in the workspace and surfaces each one as a sonner toast in the bottom-right of every page in the admin SPA. Click the toast to jump straight to the inbox row.

The bell button in the top header asks the browser for native notification permission. Once granted, every new lead also fires an OS-level notification so workspace members get pinged on tabs that aren't focused. Permission is per-domain โ€” denying it once can only be reversed from your browser's settings.

Polling pauses on hidden tabs to keep idle dashboards from burning HTTP. The cursor lives in sessionStorage so opening a second tab doesn't double-toast already-seen leads.

Email notifications

Every captured lead fans out to every workspace owner and admin over email. The notification (App\Notifications\NewLeadCaptured) is queued โ€” the visitor's HTTP request never waits on SMTP, so a slow mailer cannot slow lead capture or chat.

Two requirements for the email to actually arrive:

  1. A queue worker is running. In production we use the database driver โ€” make sure php artisan queue:work --queue=default runs on a process supervisor (the in-cluster worker takes care of this on Laravel Cloud). Without it, queued notifications pile up in jobs and never send.
  2. MAIL_MAILER + matching credentials are configured in .env. The default is log โ€” fine for dev but no actual email is sent. Switch to smtp / resend / postmark in production and verify with php artisan tinker --execute 'Mail::raw("ping",fn($m)=>$m->to("[email protected]")->subject("test"));'.

Recipients are filtered down to workspace_users.role IN ('owner', 'admin') with accepted_at IS NOT NULL. Pending invites and viewers do not receive lead emails. The footer of the email reflects your white-labelled site title (set in Settings โ†’ System).

Cleaning up the conversation log

A new Conversation row is written every time a visitor loads the widget for the first time in 24 hours. Visitors that drive by without typing still produce a row, which means /app/conversations can pile up with empty sessions over time. Two affordances keep it manageable:

  • Engaged-only filter (default). The list hides conversations with no visitor messages. Toggle Show all in the filter bar to see every session โ€” useful when you're looking for bot traffic or QA loads.
  • Per-row delete. Admin and Owner roles see a trash icon on hover. Deleting cascades to messages, leads, and applied tags via DB foreign keys; analytics events are kept (FK nullOnDelete) so aggregate stats stay intact.
  • Bulk delete empty. The button in the filter bar wipes every conversation in the workspace with no user-role message. Sessions where the visitor sent at least one message are never touched.
  • Bulk delete selected. Tick the checkboxes on any visible rows, then click Delete N selected. The workspace global scope on Conversation protects cross-tenant ID smuggling โ€” IDs from other workspaces are silently dropped server-side.

Viewers and Editors do not see delete affordances; the ConversationPolicy::delete + WorkspacePolicy::bulkDeleteConversations checks require Admin or Owner. Deletion is irreversible โ€” there is no soft-delete trail and the audit log does not yet capture conversation removals.

Audit log

Privileged actions on conversations (claim, release) write rows to the audit_logs table for forensic traceability. There's no UI page for browsing them in v1; query the table directly when you need to investigate.