Emails and messaging

What it is

Every email Payability sends to a supplier passes through the messaging service and is visible here. Emails are not fire-and-forget: many are queued pending approval, can be previewed as rendered HTML, approved or cancelled individually or as a batch, and carry their own event history.

Where it lives

Concern Location

Routes

emails/emails-routing.routes.ts — /emails/list and /emails/list/:filterId/filter. The emails/ module contains only routes.

Components

crm-tools/emails/ — email-list/, email-preview/, email-types.ts

Service

crm-tools/services/emails.service.ts (messagingUrl) with emails-status.service.ts and emails-mock.service.ts

Model

crm-tools/models/email.model.ts, batch.model.ts

Settings

/manage/email-config/list → manage/email-settings/

Supplier preferences

suppliers/services/supplier-email-preferences.service.ts, suppliers/model/email-preference.model.ts, suppliers/supplier/supplier-email-preferences/

/emails/list/:filterId/filter renders EmailPreviewComponent, while /emails/list renders EmailListComponent. The route name says "filter" but the component is the preview — the parameterised route is used to open a specific email.

Email types

crm-tools/emails/email-types.ts:

Enum member Wire value In filters Meaning

EMAIL_SUPPLIER

email_supplier

yes

Sent manually from a supplier page.

NOTE_EMAIL

email_note

yes

Generated from a CRM note.

BATCH_GROUP

batch group

yes

A group of batch emails (the container).

AUTO_EMAIL

email

yes

Automatic email, typically fired by an event rule.

TASK_NOTIFICATION

task notification

yes

Task assignment/change notification.

BATCH_EMAIL

batch

no

An individual email inside a batch group — deliberately excluded from the filter list so that batches appear once, as their group.

The wire values are inconsistent: AUTO_EMAIL is the bare string 'email', BATCH_GROUP contains a space ('batch group'). Do not derive a wire value from the enum name.

Default filter selection (EmailsStatusService.getDefaultFiltersType) is AUTO_EMAIL, NOTE_EMAIL, EMAIL_SUPPLIER, BATCH_GROUP — so task notifications are hidden until explicitly requested.

Statuses

export enum EmailStatuses { SENT = 'sent', PENDING = 'pending', CANCELLED = 'cancelled', SENDING = 'sending' }

PENDING means awaiting operator approval, not "queued for delivery" — that is SENDING. EmailsStatusService provides the predicates (isPending, isSend, isBatchGroup) and the type-name lookup; use them rather than comparing strings.

An EmailActions enum in the same file names the operations (approve, …).

Endpoints

All on messagingUrl:

Endpoint Purpose

GET email/

The email log.

POST emails/

Create/queue an email (Serialize(data, EmailModel)).

GET emails/:uuid/

One email.

PUT emails/:uuid/

Update a queued email.

DELETE emails/:uuid/

Delete.

POST emails/batch-delete/ with { uuids }

Bulk delete.

GET emails/:uuid/events/

Delivery/lifecycle events for an email.

POST email/rendered/

Render the email to final HTML.

POST email/preview/

Render a preview.

GET email/approve/:emailUuid

Approve one email.

GET email/cancel/:emailUuid

Cancel one email.

GET email/batch_approve/:batchUuid

Approve a whole batch.

GET email/batch_cancel/:batchUuid

Cancel a whole batch.

POST email/batch/

Create a batch send.

Approve and cancel are GET requests that mutate state. They are not safe or idempotent in the HTTP sense. Do not prefetch them, do not put them behind a link crawler, and be careful adding them to any retry logic.

emails.service.ts also carries its own headers object (instance id, target UUID) in the same style as CrmService.

Two render paths

email/rendered/ and email/preview/ both produce HTML from an EmailModel. preview is the operator-facing "what will this look like" call; rendered produces the final artefact. Both take the full model, so a preview reflects unsaved edits.

crm-tools/shared/b2b-preview/ and the TinyMCE editor (shared/editor/) are the composition surface.

Batch email

A batch group is a single logical send to many recipients:

  • POST email/batch/ creates it.

  • It appears in the timeline as BATCH_GROUP; the individual BATCH_EMAIL items hang off it.

  • email/batch_approve/:batchUuid and email/batch_cancel/:batchUuid act on the whole group.

  • The CRM store keeps a separate batchEmails stream, loaded by FETCH_CRM_BATCH_EMAILS and read through selectCrmBatchEmails.

Bulk email creation from a CSV is a Manage operation — see bulk actions.

Email settings (Manage)

/manage/email-config/list administers templates and configuration. manage/email-settings/:

Folder Purpose

email-settings-list/

The configuration list.

email-add-new-config-dialog/

Add a configuration.

email-edit-config-dialog/

Edit a configuration.

email-edit-variables/

Edit the template variables available to that email.

delete-email-setting/

Delete.

email-type-badge/

The type badge shown in the list.

email-form-fields.ts

The form field definitions.

Gated by the INT_EMAIL_SETTINGS permission.

Per-supplier email preferences

A supplier can opt out of categories of email. SupplierEmailPreferencesService on internalApiUrl:

GET suppliers/:supplierKey/email_preference   → { emailPreference: EmailPreference[] }
PUT suppliers/:supplierKey/email_preference   ← { emailPreferenceCode, isEnabled }
export interface EmailPreference {
  code: string;         // e.g. "DAILY_EMAILS"
  name: string;
  description: string;
  enabled: boolean;
}

Note the endpoint path is singular email_preference while the response property is emailPreference wrapping an array, and the update sends one preference at a time (emailPreferenceCode + isEnabled) rather than the whole set.

The supplier record also carries a coarse sendAutoEmail boolean, independent of these per-code preferences.

This feature has a spec in .specs/email-preference-management.md — one of the two reference spec formats. See the spec-driven workflow.

Emails as an event-rule action

send-email is one of the four active event-rule action types (manage/event-rules/rules-add-edit/action-form/send-email/). A rule matches a domain event, applies filters, and sends a templated email with context and data mappings. That is how most AUTO_EMAIL traffic originates. See events.

SMS

Text messaging shares the messaging backend but not this UI. suppliers/supplier/supplier-onboarding/smsnumbers/sms-numbers-provider.service.ts spans authToolsApiUrl (provisioning the number) and messagingUrl (sending). See onboarding.

Access

/emails denies REST_UW_ACCESS and ADVANCE_AUDITORS, both on the route and in the sidebar. INT_EMAIL_SETTINGS governs the Manage screens.

Gotchas

Issue Detail

GET mutates

Approve and cancel, single and batch.

Enum values are not derivable

'email' for AUTO_EMAIL; 'batch group' contains a space.

PENDING is approval, not delivery

SENDING is delivery.

BATCH_EMAIL is filtered out by design

Batches surface as BATCH_GROUP.

The emails/ module has no components

It is a routing shim over crm-tools/emails/.

Route name/component mismatch

list/:filterId/filter renders the preview component.

Two email models share one class

EmailModel is used for email, email_supplier and batch items in CrmService.listActions.