Skip to main content

Communications Inbox Product Contract

Purpose

The communications inbox is the human back-and-forth layer that connects client-owned work across the organization app and client portal. It is not a second task system, a free-standing chat product, or another presentation of system notifications.

The canonical mental model is:

Shared conversation threads, personal inbox state, and current access checked on every read, reply, and email delivery.

Tasks, requests, files, updates, and agreements remain the owning business records. A communication thread supplies their common conversation history and inbox projection.

Product boundaries

Communications versus notifications

  • Communications are messages written by an organization member or client and are expected to support a reply.
  • Notifications report system activity, deadlines, lifecycle changes, billing events, or other one-way signals.
  • A source event may produce both kinds when both outcomes are useful, but the inboxes must not contain duplicate cards for the same human message.
  • The communications drawer only lists communication entries. The notifications drawer excludes them from its primary mental model.

Threads versus inbox entries

  • One communicationThread is the shared history for one source record.
  • One notificationInbox communication entry is the personal projection of that thread for one user and one surface.
  • Read and archive state belong to the personal projection. They are never shared across participants.
  • Replying adds one message to the shared thread and updates each currently participating person's projection.
  • A new message restores an archived personal projection for active participants. Archiving is therefore inbox cleanup, not unsubscribe or shared resolution.

Participant rules

Participation determines who should receive a conversation. Authorization is a separate, stricter check. Being recorded in a historical participant array never overrides current organization membership, client assignment, portal grant, portal scope, tenant selection, or source visibility.

SourceOrganization participantsClient participants
Project taskProject owner, task assignee, mentioned members, organization authorsActive full or delivery portal users when both project and task are client-visible
Client support/work/project requestOrganization owner, assigned request owner, organization respondersThe client user who submitted the request
File requestOrganization owner, project owner when present, requester, organization respondersActive full or delivery portal users
Project updateOrganization owner, project owner, configured relationship contactsActive full or delivery portal users
Shared drive itemOrganization owner, project owner when present, configured relationship contactsActive full or delivery portal users
Published contractOrganization owner, project owner when present, configured relationship contactsActive portal users because Agreements is available to every portal scope

The quote source type is reserved by the communication contract. When a quote producer is connected, only full and billing portal users may participate.

Participant changes follow these rules:

  • Organization participation is additive so a member who took part in a conversation retains its history while they remain an active member with access to that client.
  • Current membership and client assignment are still revalidated. Removing a member or their client access removes inbox, drawer, and email access immediately even if their historical participant ID remains.
  • Current portal grants and scopes are revalidated. Disabling a grant, changing delivery access to billing, disabling the portal, or switching the active client company takes effect immediately.
  • Mentions route attention among already eligible organization members. A mention must never bypass tenant or client access.
  • Granting a new person access does not manufacture unread work from before they joined. They become an inbox participant on the next eligible conversation event; the owning record remains their historical source of truth.

Client portal behavior

The client portal is a shared client workspace, not one shared mailbox.

  • Eligible client participants see the same thread history.
  • Each signed-in client user has independent read and archive state.
  • One client user's read or archive action cannot clear another user's inbox.
  • A support, work, or project request stays with its requester unless an explicit future client-sharing feature expands it.
  • Delivery conversations are not visible to billing-only users.
  • Agreements are visible to all portal scopes; quotes are excluded from delivery-only access.
  • In a multi-company portal identity, the drawer only exposes conversations for the currently selected client account.

Organization behavior

  • Each organization member has a personal communications inbox.
  • A member sees only threads in which they participate and only while they have current access to the owning client.
  • Reassignment does not erase conversation history for a former participant who still has legitimate client access.
  • Removing organization membership or client assignment immediately removes drawer, reply, and email access.
  • A future team-supervision view may expose broader routing, but V1 does not turn every client conversation into organization-wide noise.

Source visibility and lifecycle

Portal authorization rechecks the owning source every time:

  • Project tasks require a client-visible task inside a client-visible, non-archived project.
  • Project updates require published, non-archived updates.
  • Drive conversations require a shared, non-archived, non-folder item.
  • Contracts require published and shared state.
  • Quotes require a non-draft state.
  • Client and file requests must still exist and belong to the same organization and client account as the thread.

If the source is hidden, unpublished, archived where applicable, deleted, moved to another tenant/client, or otherwise becomes ineligible, the portal inbox and thread fail closed. Organization history may remain available to authorized staff for audit continuity.

A task with conversation history cannot be permanently deleted. Other source types follow their existing published-history, submission-history, or archival retention rules.

Message and inbox state rules

  • Empty messages are rejected after trimming.
  • Messages are capped at 10,000 characters.
  • Source message keys and provider IDs make source projection and inbound email processing idempotent.
  • The author sees their own sent message as read.
  • Every other active participant receives an unread projection, including same-side colleagues.
  • Opening the conversation marks only the viewer's projection read.
  • Archiving marks only the viewer's projection archived and read.
  • A later message unarchives the projection for current participants.
  • A reply reopens a resolved thread. Shared resolve controls are not exposed in V1; personal archive is the shipped completion gesture.
  • An older migrated or delayed source message is retained in chronological history but cannot replace a newer thread preview, sort timestamp, or inbox state.

Email delivery and replies

Email is a transport for the same thread, not a parallel conversation.

  • Each non-author participant may receive an email after an in-app message.
  • Organization delivery honors customerActivityEmails.
  • Portal delivery honors supportEmails.
  • Disabling email does not remove the in-app conversation.
  • Email is sent only after current membership/grant, client access, participant status, portal scope, and source visibility are revalidated.
  • Each Reply-To alias is signed for the exact thread, recipient user, and surface. Separate organization and portal identities may therefore use the same email address without replies being attributed to the wrong realm.
  • The Postmark inbound webhook verifies deployment-owned HTTP Basic authentication, validates the application-signed alias, checks the sender address against that exact recipient identity, revalidates current access, removes common quoted history, and appends one message to the shared thread.
  • Forwarded replies, revoked participants, stale scopes, altered aliases, duplicate webhook events, and unknown senders fail closed.
  • Email replies mirror back into task comments or client-request messages where that owning source supports native messages.
  • Provider MessageID values are deduplication keys. Retries are safe and a duplicate delivery cannot append a second human message.

Live reply-by-email additionally requires the deployment configuration listed in docs/dev/integrations/optional-features.md.

Retention and privacy

  • Thread messages are retained with the owning business history unless the source's explicit retention policy permits deletion.
  • Account purge keeps the work history but replaces the author's snapshot name with Deleted user and removes stored author and inbound-receipt email data.
  • Deleting an inbox projection never deletes the shared thread or its messages.
  • A deleted or currently unauthorized user cannot receive new email or submit an email reply through a retained participant reference.

Non-goals for V1

  • No unlinked direct messages or free-standing channels.
  • No shared read/archive state.
  • No organization-wide or client-wide supervisor mailbox.
  • No automatic participant expansion merely because someone can view the client account.
  • No client-private side conversation inside an organization thread.
  • No inbound email attachments in V1. Postmark attachment payloads are discarded; clients use the owning task or file request when a file must become project data.
  • No replacement for the owning task, request, file, agreement, or project lifecycle.

Release invariants

The feature is correct only while all of the following remain true:

  1. One source record maps to at most one thread per organization and source type.
  2. One user and surface maps to at most one inbox projection per thread.
  3. Read and archive changes never affect another participant.
  4. Current access is checked independently of historical participation.
  5. Portal source visibility is checked on list, count, open, reply, outbound email, and inbound email paths.
  6. Email identity is bound to both recipient and account realm.
  7. Duplicate source events and email webhooks cannot duplicate messages.
  8. Historical backfill cannot move a thread backward in time.
  9. Account purge removes retained communication email identity data.
  10. Human communication and system notifications remain separate inbox mental models.

Implementation map

  • Thread and message model: packages/backend/convex/communications/model.ts
  • Current-access and source checks: packages/backend/convex/communications/access.ts
  • Source participant adapters: packages/backend/convex/communications/sources.ts
  • Organization API: packages/backend/convex/org/communications.ts
  • Portal API: packages/backend/convex/portal/communications.ts
  • Email transport and inbound processing: packages/backend/convex/communicationsNodeActions.ts and packages/backend/convex/communications.ts
  • Personal inbox projections: packages/backend/convex/notifications/inbox.ts
  • Shared drawer UI: apps/app/src/app/[locale]/_components/communications/communication-inbox-sheet.tsx
  • Regression coverage: packages/backend/tests/communications/thread-model.test.ts