Some checks are pending
Bidi Control Character Guard / bidi-control-guard (push) Waiting to run
Circular Dependency Check / Check for new circular dependencies (push) Waiting to run
Citus Migration Smoke / Combined migrations on single-node Citus (push) Waiting to run
E2E Fresh Install Tests / fresh-install-e2e (push) Waiting to run
ext-v2 guardrails / Run ext-v2 guard and ESLint (push) Waiting to run
Integration Tests / Check for relevant changes (push) Waiting to run
Integration Tests / ${{ (github.event_name == 'schedule' || github.event.inputs.suite == 'full') && 'Full integration suite' || 'Tier-1 integration subset' }} (push) Blocked by required conditions
Mobile checks / Mobile lint + typecheck (push) Waiting to run
Mobile checks / Mobile unit tests (push) Waiting to run
Mobile checks / Mobile dependency audit (report) (push) Waiting to run
Mobile checks / Mobile reproducibility checks (push) Waiting to run
Secrets guard (env backups) / Ensure no tracked env backup files (push) Waiting to run
Temporal Readiness / fast-readiness (push) Waiting to run
Temporal Readiness / docker-parity (push) Waiting to run
TypeScript Type Check / Nx affected typecheck (push) Waiting to run
Unit Tests / Skipped-test budget (push) Waiting to run
Unit Tests / Nx affected unit tests (push) Waiting to run
Unit Tests / Server unit coverage (informational) (push) Waiting to run
Validate Tenant Management Schema / Check for relevant changes (push) Waiting to run
Validate Tenant Management Schema / Validate Tenant Management Schema (push) Blocked by required conditions
EE Workflows Build Guard / ee-workflows-build-guard (push) Waiting to run
Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz Source: /opt/alga-psa on psa.joliet.tech
6.8 KiB
6.8 KiB
PRD — Ticket Origin Badge (Internal, Client Portal, Inbound Email, API)
- Slug:
2026-02-09-ticket-origin-badge - Date:
2026-02-09 - Status: Draft
Summary
Add a ticket-level origin badge so users can see where a ticket was created:
Internal (MSP)Client PortalInbound EmailAPI
The badge should be visible in ticket details and should work consistently for both MSP and client portal views.
Problem
Today, users can see ticket response state and per-comment response source, but not the ticket creation origin at a glance. This makes triage and context handoff slower, especially when teams handle mixed intake channels.
Goals
- Show a clear ticket origin badge in ticket details.
- Support four first-class origins:
internal,client_portal,inbound_email,api. - Persist ticket origin at creation time so we do not rely only on heuristics.
- Keep a fallback resolver for legacy tickets created before origin persistence.
- Keep origin storage extensible for future values (for example
ai_agent) without schema redesign.
Non-goals
- No ticket list/table filtering by origin in this phase.
- No redesign of ticket details layout beyond adding the badge.
- No changes to existing per-comment
responseSourcebadges in conversation. - No analytics/reporting dashboard for ticket origin in this phase.
Users and Primary Flows
Flow A — MSP user creates a ticket internally
- Internal user creates ticket from MSP app (
addTicket/ related internal flows). - Ticket origin resolves to
internal. - Badge shows internal origin in ticket details.
Flow B — Client creates a ticket in client portal
- Client user creates ticket from client portal (
createClientTicket). - Ticket origin resolves to
client_portal. - Badge shows client portal origin in ticket details.
Flow C — Inbound email creates a new ticket
- Inbound email processing creates ticket (
processInboundEmailInApp/createTicketFromEmail). - Ticket origin resolves to
inbound_email. - Badge shows inbound email origin in ticket details.
Flow D — External integration creates a ticket through API
- API request creates ticket (
POST /api/v1/tickets->ApiTicketController->TicketService). - Ticket origin resolves to
api. - Badge shows API origin in ticket details.
UX / UI Notes
- Badge is ticket-level (not per-comment) and indicates creation origin.
- Placement:
- MSP: in
packages/tickets/src/components/ticket/TicketDetails.tsxheader, near ticket number and response-state badge. - Client portal: in
packages/client-portal/src/components/tickets/TicketDetails.tsxheader/status area.
- MSP: in
- Proposed labels:
Created InternallyCreated via Client PortalCreated via Inbound EmailCreated via API
- Badge should always render exactly one origin value for valid tickets.
- Unknown future origin values should render safely with a generic fallback label (
Created via Other) until a specific label is added.
Requirements
Functional Requirements
- Add persistent
ticket_originfield onticketstable (text). - Canonical first-class values for this phase:
internal | client_portal | inbound_email | api. - Add a shared normalization/helper (
normalizeTicketOrigin/getTicketOrigin) used by both MSP and client portal details paths. - Origin resolution precedence for reads:
- If
tickets.ticket_originexists, use it. - Else use legacy fallback resolver (email metadata, creator user type, etc.).
- Else resolve
internal.
- If
- Set
ticket_originexplicitly on create paths:- MSP app creates ->
internal - Client portal creates ->
client_portal - Inbound email creates ->
inbound_email - API creates ->
api
- MSP app creates ->
- Ensure ticket detail actions return derived origin for UI consumption:
packages/tickets/src/actions/ticketActions.ts#getTicketByIdpackages/client-portal/src/actions/client-portal-actions/client-tickets.ts#getClientTicketDetails
- Add a shared
TicketOriginBadgecomponent in@alga-psa/tickets/components. - Render badge in both ticket detail UIs using shared labels/translation keys.
- Keep existing comment-level response source behavior unchanged.
Non-functional Requirements
- Backward compatible with existing tickets (legacy tickets without
ticket_originstill resolve correctly). - No additional heavy queries; origin derivation should be O(1) per ticket details load.
- Resolver must be deterministic and unit-testable.
Data / API / Integrations
- Existing signals already available in current codebase:
tickets.email_metadata(inbound email-created tickets)tickets.entered_by+ creatorusers.user_type- Existing create path context:
- MSP app currently passes
source: 'web_app' - Client portal passes
source: 'client_portal' - Inbound email paths pass
source: 'email' - API service passes
source: 'api'
- MSP app currently passes
- Existing code pointers:
- Internal create:
packages/tickets/src/actions/ticketActions.ts - Client portal create:
packages/client-portal/src/actions/client-portal-actions/client-tickets.ts - Inbound email create:
shared/services/email/processInboundEmailInApp.ts,shared/workflow/actions/emailWorkflowActions.ts - API create:
server/src/app/api/v1/tickets/route.ts,server/src/lib/api/services/TicketService.ts
- Internal create:
Security / Permissions
- No new permissions required.
- Origin badge displays metadata about ticket origin only; it does not expose sensitive payloads.
Observability
- No new metrics/logging required for MVP.
Rollout / Migration
- DB migration to add
tickets.ticket_origin. - Data backfill (migration or one-time script) for existing rows:
email_metadatapresent ->inbound_email- creator user type
client->client_portal - otherwise ->
internal
- Keep legacy fallback resolver in code as a safety net.
Open Questions
- Confirm final internal label copy:
Created InternallyvsCreated by MSP. - Should this badge remain ticket-details-only, or also appear in ticket list rows now?
- Should inbound email badge stay generic, or include provider label (
Gmail,Microsoft,IMAP) in this phase? - For future origins like
ai_agent, should we show raw value with title-case fallback or keep a strict whitelist + genericOtherlabel? - Do we want to expose ticket origin in exports/reporting in this phase (currently no)?
Acceptance Criteria (Definition of Done)
- New ticket created internally shows
internalorigin badge in MSP ticket details. - New ticket created in client portal shows
client_portalorigin badge in both MSP and client portal ticket details. - New inbound email-created ticket shows
inbound_emailorigin badge in ticket details. - New API-created ticket shows
apiorigin badge in ticket details. - Legacy tickets still render a stable origin badge using fallback rules.
- Existing comment response-source badge behavior is unchanged.