Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz Source: /opt/alga-psa on psa.joliet.tech
16 KiB
PRD — Workflow Client Actions
- Slug:
2026-04-25-workflow-client-actions - Date:
2026-04-25 - Status: Draft pending scope confirmation
Summary
Add the missing Client module workflow actions to Workflow Runtime V2 so workflow authors can create, update, archive, delete, duplicate, tag, link to tickets, note, and log interactions for clients from the Workflow Designer catalog.
The new actions should live in shared/workflow/runtime/actions/businessOperations/clients.ts, register through the existing registerClientActions() path, and appear automatically in the existing Client group of the grouped workflow action catalog via the clients.* action id prefix.
Problem
The Client module currently exposes only read-oriented workflow actions:
clients.findclients.search
Workflow authors can locate clients, but they cannot perform common client lifecycle mutations without falling back to generic/non-client actions or custom code. This creates an uneven workflow authoring experience compared with the Ticket module, which already exposes create/update/assign/comment/link operations.
Goals
- Add Client module actions for:
- edit client
- create client
- archive client
- delete client
- duplicate client
- add tag to client
- assign client to ticket
- add note to client
- add interaction to client
- Keep action registration consistent with the current business operations architecture.
- Use Zod input/output schemas that work with the Workflow Designer input editor and downstream output pickers.
- Enforce tenant scoping, permissions, validation, audit logging, and idempotency consistently with existing business operation actions.
- Reuse existing model/service behavior where safe so workflow actions match product behavior.
- Keep this as a workflow-action expansion only; do not redesign the broader client UI, API, or event system.
Non-goals
- Building new Workflow Designer UI components beyond schema metadata needed for existing pickers.
- Adding new catalog groups or changing catalog grouping behavior.
- Reworking client CRUD data models, billing setup, client portal onboarding, or deletion dependency rules.
- Adding bulk client actions.
- Replacing existing generic CRM activity note action (
crm.create_activity_note). - Adding production observability/metrics beyond existing action logging/audit patterns unless requested later.
Users and Primary Flows
Workflow author
- Opens the Workflow Designer.
- Selects the Client group.
- Adds one of the new Client actions.
- Configures fixed values or mappings with existing picker-backed fields where applicable.
- Saves, publishes, and runs the workflow.
Workflow runtime
- Executes an
action.callstep. - Resolves mapped inputs and secrets.
- Calls the selected
clients.*action handler. - Validates inputs and outputs.
- Saves action output into the workflow envelope when
saveAsis configured.
MSP user represented by a workflow run
- The runtime resolves the workflow actor user.
- Actions run only if that actor has the required MSP permission for the target operation.
UX / UI Notes
The new actions should appear under the existing Client catalog group; no catalog seed changes should be needed because shared/workflow/runtime/designer/actionCatalog.ts groups clients.* under Client.
Action labels should be clear and parallel to the user's requested verbs:
clients.create→ Create Clientclients.update→ Edit Clientclients.archive→ Archive Clientclients.delete→ Delete Clientclients.duplicate→ Duplicate Clientclients.add_tag→ Add Tag to Clientclients.assign_to_ticket→ Assign Client to Ticketclients.add_note→ Add Note to Clientclients.add_interaction→ Add Interaction to Client
Picker metadata should be added where useful:
- client id fields:
x-workflow-picker-kind: client - ticket id fields:
x-workflow-picker-kind: ticketif supported by the picker registry; otherwise use a UUID field and note the limitation - contact id fields for interactions:
x-workflow-picker-kind: contactwithclient_iddependency - interaction type/status fields if the existing designer picker registry supports them; otherwise use UUID fields
Requirements
Functional Requirements
clients.create
- Create a tenant-scoped client.
- Minimum required input:
client_name. - Support the commonly used client fields already present in shared client interfaces/model validation:
client_nameclient_typeurlphone_noemail/ billing email mapping decision pendingaddress,address_2,city,state,zip,countrydefault_currency_codenotespropertiesparent_client_idcontract_line_idis_defaultonly if existing product behavior safely supports it; otherwise omit
- Optionally accept
tagsfor initial client tags. - Return a client summary suitable for downstream steps.
- Use action-provided idempotency key to prevent duplicate clients when a workflow retries.
clients.update
- Update a tenant-scoped client by
client_id. - Accept a
patchobject for editable client fields. - Reject empty patches.
- Preserve existing behavior for setting clients inactive, including associated contact/client-user deactivation if reusing
ClientService.updateis feasible; otherwise explicitly document any difference before implementation. - Optionally accept tag replacement only if the action name/schema makes replacement semantics obvious; otherwise leave tagging to
clients.add_tag. - Return before/after or updated client summary plus changed fields.
clients.archive
- Archive/deactivate a tenant-scoped client by setting
is_inactive = truethrough the same semantics as editing the client inactive. - Required input:
client_id. - Preserve the client record and historical relationships.
- Deactivate associated contacts and client users if this matches the existing
ClientService.update(..., { is_inactive: true })behavior or an equivalent shared implementation. - Return client id, archived flag, previous inactive state, and archived timestamp.
- Treat already-inactive clients as a successful idempotent no-op by default.
clients.delete
- Hard-delete a client by
client_idusing the same dependency checks as product/API deletion. - Require
confirm: trueas an explicit destructive-action guard. - Refuse deletion for default clients and clients with blocking dependencies.
- Clean related client-owned artifacts using existing service/helper behavior where possible.
- Return
{ deleted: true, client_id }on success. - Treat missing clients according to an explicit
on_not_foundoption (errordefault, optionalreturn_false).
clients.duplicate
- Create a new client using an existing client as a template.
- Required inputs:
source_client_idclient_namefor the duplicate, or a deterministic default naming strategy if confirmed
- Copy safe profile fields such as client type, URL, phone, billing email/email, address fields, default currency, properties, and notes by default.
- Provide explicit copy options for related records:
copy_tagsdefaulttruecopy_locationsdefaultfalse
- Do not copy contacts, notes documents, billing contracts, invoices, tickets, projects, interactions, payment customer records, portal users, or external integration mappings in v1.
- Return source and duplicate client summaries.
clients.add_tag
- Add one or more tags to a client.
- Create missing
tag_definitionsfortagged_type = 'client'. - Insert missing
tag_mappingsidempotently. - No-op existing mappings by default rather than failing on duplicates.
- Return added/existing tag summaries and counts.
clients.assign_to_ticket
- Set a ticket's
client_idto the selected client. - Validate that the ticket and client both exist in the tenant.
- Optional inputs:
contact_idto set/replace ticket contact, or explicitnullto clear it, with validation that a non-null contact belongs to the selected clientlocation_idto set/replace ticket location, or explicitnullto clear it, with validation that a non-null location belongs to the selected clientcommentorreasonfor an optional internal comment/audit detail, only if consistent with ticket update patterns
- Preserve omitted contact/location fields; only update or clear them when the workflow input explicitly provides the field.
- Return ticket id, previous client id, new client id, previous/current contact id, and previous/current location id.
clients.add_note
- Add or save client note content on the client's notes document.
- Use the existing client notes document pattern (
clients.notes_document_id,createBlockDocument,updateBlockContent) where feasible. - Append a new workflow-created paragraph-like block to the client notes document. Do not replace the entire notes document in this action.
- Publish or preserve the existing NOTE_CREATED behavior if creating a new notes document.
- Return document id, created/updated timestamp, and client id.
clients.add_interaction
- Log an interaction linked to a client.
- Required inputs:
client_idtype_idtitle
- Optional inputs:
contact_idticket_idnotesstart_timeend_timedurationstatus_idinteraction_date
- Validate contact/ticket relationships when provided.
- Use the current workflow actor as
user_idin v1. Do not expose auser_idoverride in the initial action schema. - Decision rationale: interactions are attribution-bearing business records. Allowing arbitrary
user_idinput could misattribute activity to another MSP user unless the broader permission model explicitly supports "create interaction for another user" semantics. The workflow actor is already resolved for permissions/audit, so using that actor keeps v1 safe and predictable. - Future goal: add a permission-gated user override in a later action version if product needs workflows to log interactions on behalf of another active internal user. That future version should define the exact permission/admin requirement, validate the target user, and preserve clear audit attribution for both the workflow actor and target interaction user.
- Set a default interaction status when omitted, matching existing
addInteractionbehavior. - Publish or preserve existing INTERACTION_LOGGED behavior if feasible.
- Return interaction summary.
Non-functional Requirements
- Fail fast with clear
throwActionErrorcategories/codes for validation, not-found, permission, conflict, and transient failures. - Avoid silently swallowing tag/tax/note setup failures in workflow actions unless the existing product behavior explicitly does so and the behavior is documented.
- Keep action outputs stable and typed for downstream workflow expression use.
- Keep implementation in shared workflow runtime code where the existing business operation actions live.
Data / API / Integrations
Key existing files and patterns:
shared/workflow/runtime/actions/businessOperations/clients.ts— currentclients.findandclients.search; target file for new actions.shared/workflow/runtime/actions/businessOperations/shared.ts— tenant transaction, permissions, audit, errors, idempotency helpers.shared/models/clientModel.ts— shared client create/update/get helpers and validation.server/src/lib/api/services/ClientService.ts— API service with richer create/update/delete/tag behavior and workflow event publication.packages/clients/src/actions/clientNoteActions.ts— client notes document behavior.packages/clients/src/actions/interactionActions.tsandpackages/clients/src/models/interactions.ts— interaction creation behavior.shared/workflow/runtime/actions/businessOperations/crm.ts— existing generic CRM activity note action.shared/workflow/runtime/actions/businessOperations/tickets.ts— ticket update/link/comment/idempotency/picker patterns.
Expected tables touched:
clientscontactsticketsclient_locationstag_definitionstag_mappingsdocumentsdocument_block_contentdocument_associationsinteractionsinteraction_types/system_interaction_typesstatusesfor interaction defaultsaudit_logs
Security / Permissions
Proposed permission checks:
clients.create→client:createclients.update→client:updateclients.archive→client:updateclients.delete→client:deleteclients.duplicate→client:readon source andclient:createfor duplicateclients.add_tag→client:updateclients.assign_to_ticket→client:readandticket:updateclients.add_note→client:updateclients.add_interaction→client:updateor a more specific interaction permission if one exists; verify before implementation
All queries and mutations must include the current tenant. Cross-tenant ids must behave as not found.
Rollout / Migration
No database migration is expected for the baseline plan. The work is primarily action registration and handler implementation.
If duplicate-client behavior requires cloning related records not currently supported by shared helpers, implement only safe direct-table duplication for explicitly selected related records or defer those copy options.
Open Questions
- Resolved: use
clients.updateas the durable action id for the EDIT client action, with UI label "Edit Client", to match CRUD/runtime naming while preserving user-facing wording. - Resolved:
clients.add_noteappends a workflow-created note block to the client notes document. It does not replace the document and does not create an interaction/activity note. - Resolved:
clients.duplicaterequires an explicit duplicateclient_name, copies safe core profile fields, copies tags by default, supports optionalcopy_locations, and does not copy contacts or notes in v1. - Resolved:
clients.deleteis validated hard delete, andclients.archiveis added now as a separate archive/deactivate action. - Resolved:
clients.assign_to_ticketpreserves omitted contact/location fields. Explicitnullclears those fields; non-null values are validated against the selected client. - Resolved:
clients.add_interactionalways uses the workflow actor asuser_idin v1. Future versions may add a permission-gated target-user override after the exact permission and audit semantics are designed.
Acceptance Criteria (Definition of Done)
- The Client catalog group includes all nine new actions plus existing find/search actions.
initializeWorkflowRuntimeV2()registers the new actions through the existing business operations registration path without extra bootstrap changes.- Each new action has a versioned action definition with input schema, output schema, UI metadata, side-effect metadata, idempotency metadata, and handler.
- Each handler validates tenant-scoped entity existence before mutation.
- Each handler enforces the proposed permissions or an explicitly confirmed replacement permission.
- Create, duplicate, tag, note, and interaction actions are retry-safe through action-provided idempotency keys or idempotent no-op semantics.
- Delete uses existing client deletion dependency/default-client guardrails.
- Assign-to-ticket validates client/contact/location relationships and preserves omitted ticket fields.
- Notes and interactions follow existing product data shapes closely enough to render in existing client detail screens.
- Tests cover registration/catalog exposure, schema metadata, successful DB-backed mutations, and high-risk failure cases.