Discussion Hub — External Communication
Discussion Hub — External Communication
The Discussion Hub is the centralised interface for email-based external communication. Discussions allow your team to send messages to clients, freelancers, and external contacts directly from NexusRMS, with full email threading, delivery tracking, and internal-only notes.
What Are Discussions?
Discussions are CommunicationThread records with thread_type set to discussion. Unlike internal channels and DMs, discussion messages are delivered as emails to external recipients. Replies are threaded back into the same conversation using email Message-ID and In-Reply-To headers.
Discussion Types (13 Total)
Every discussion is assigned a discussion_type for categorisation and filtering:
- Core types (6) — quote, invoice, document, freelancer, project, general
- Project-specific types (7) — client, crew, equipment, logistics, venue, change, issue
Creating a Discussion
Discussions are created via CommunicationThread::createDiscussion() with the following parameters:
- creatorId — The staff member's user ID (automatically assigned owner role)
- subject — The email subject line for the thread
- discussionType — One of the 13 types above (defaults to general)
- contextType / contextId — Optional polymorphic link to a Quote, Invoice, Project, or Client record
- initialMessage — Optional first message; if provided, message_count, last_activity_at, and last_message_preview are updated immediately
Participants
Discussion participants can be internal users, external email addresses, or freelancer node contacts. Key fields on CommunicationParticipant:
- user_id — Internal user ID (null for external participants)
- email — Email address for external participants
- role — Participant role: owner, participant, cc, or bcc
- freelancer_node_id — Links to a Freelancer Node for cross-database communication
- is_external_freelancer — Boolean flag for freelancers from the Nodes system
Email Threading
Each discussion generates a unique email_thread_id in the format <[email protected]>. Outbound emails include a Message-ID header (stored in email_message_id) and replies reference it via In-Reply-To (stored in email_in_reply_to). This ensures Gmail, Outlook, and other clients group replies into the same conversation.
Internal vs External Messages
Each CommunicationMessage has an is_internal boolean flag:
- is_internal = false — Visible to all participants; sent via email to external recipients
- is_internal = true — Staff-only note; never included in outbound emails
Email Tracking (DiscussionEmailTracking)
Every outbound email creates a tracking record per recipient with the following lifecycle fields:
- sent_at / delivered_at — Dispatch and server-confirmed delivery timestamps
- opened_at / open_count — First open timestamp and total open count (tracked via a 1x1 transparent pixel per recipient, identified by tracking_pixel_id)
- clicked_at / click_count — First link click timestamp and total click count
- bounced_at / bounce_type / bounce_reason — Bounce timestamp with type: hard (permanent), soft (temporary), or complaint (spam report)
Recipient Types
- client_contact (RECIPIENT_CLIENT_CONTACT) — A contact from your client CRM
- freelancer (RECIPIENT_FREELANCER) — A freelancer connected via Nexus Nodes
- external (RECIPIENT_EXTERNAL) — Any other external email address
API Routes
The Discussion Hub is powered by tenant API endpoints under /api/tenant/threads:
| Method | Endpoint | Purpose |
|---|---|---|
| GET / POST | /api/tenant/threads | List all discussions (with filtering) or create a new one |
| GET | /api/tenant/threads/{id} | View a discussion with messages and participants |
| POST | /api/tenant/threads/{id}/messages | Add a message (internal or external) |
| POST | /api/tenant/threads/{id}/send | Send to external recipients via email |
| POST | /api/tenant/threads/{id}/attachments | Attach a document to the discussion |
| GET | /api/tenant/threads/{id}/tracking | Retrieve email tracking statistics |
| POST | /api/tenant/threads/{id}/archive | Archive the discussion |
| GET | /api/tenant/threads/unread-count | Total unread discussion count |
| GET | /api/tenant/threads/category-counts | Counts broken down by discussion type |
| GET | /api/tenant/threads/by-context/{type}/{id} | Discussions linked to a specific entity |
Archiving and Status Management
Discussions follow the same three-status lifecycle as all communication threads: active (open for messages), archived (hidden from default view, accessible via filter), and closed (locked, no new messages). Use the archive endpoint to move completed discussions out of the active view. Archived discussions can be reopened at any time via the reopen() method.
Was this article helpful?