Technical reference for the templates system — covers template sets, triggers, database schema, communication workflow, language support, and admin interfaces.
Technical Reference — app.diagnostic.ly
The templates system is responsible for all outbound communications on the platform — emails and SMS messages sent to assignees (patients), account admins, observers, managers, and system users. Every automated communication is driven by a template stored in the database, rendered with dynamic content, and delivered via an asynchronous email queue.
The system is multi-tenant: template sets are created and managed exclusively by global admins, and they support multiple languages through a copy-and-translate workflow. Accounts do not author or manage templates — they are simply assigned a messaging set by a global admin, and all communications sent on their behalf are driven by that set.
messagingA template set is the top-level container. It holds the branding and sender configuration for a group of templates — the “from” name and email address, logo, website URL, provisioning URL, support phone number, and email signature. Every template in the system belongs to exactly one template set.
accounts.messaging_id. From that point forward, all communications sent on behalf of that account are resolved from the assigned set.Template sets can be:
account_id = 0) — available to all accounts as a shared baselineaccount_id = N) — created by a global admin and scoped to one accountWhen an event triggers a communication, the platform resolves which template set to use by looking up the account’s messaging_id.
messaging_typeA trigger (called a “messaging type”) is a named event in the platform workflow that causes a communication to be sent. Examples:
Assignee - Kit ShippedAccount Admin - Order PlacedAuthentication - 2FA Authentication MessageAssignee - Test Results Available NotificationEach trigger has a stable integer id. Application code references triggers exclusively by this numeric ID — the message_purpose string is for human display only.
message_templatesA template is the actual content of a communication. It belongs to a template set and is linked to a specific trigger. Each trigger can have at most one template per combination of:
messagingID)templateType: 1 = Email, 2 = SMS)language_id)A single trigger may have up to two templates per language in any given template set (one email, one SMS).
messaging — Template Sets| Column | Type | Description |
|---|---|---|
id | int | Primary key |
messaging_set_name | varchar | Display name |
name | varchar | Website/brand name |
email_from_name_for_purchasers | varchar | From name for account admin emails |
email_from_address_for_purchsers | varchar | From address for account admin emails |
email_from_name_for_assignees | varchar | From name for assignee emails |
email_from_address_for_assignees | varchar | From address for assignee emails |
websiteURL | varchar | Patient-facing portal URL |
provisionURL | varchar | Provisioning/admin portal URL |
email_signature | varchar | Appended to all outbound emails |
logoURL | varchar | Logo file path (relative to assets/logo/) |
faviconURL | varchar | Favicon file path |
supportPhone | varchar | Support phone shown in templates |
account_id | int | 0 = global; otherwise FK to accounts.id |
allow_accounts_to_select | tinyint | Whether accounts can self-assign this set |
messaging_type — Triggers| Column | Type | Description |
|---|---|---|
id | int | Primary key; referenced by application code as messageTypeID |
message_purpose | varchar | Human-readable trigger name |
sortorder | int | Display sort order |
language_id | int | FK → languages.id |
message_template_groupings_id | int | FK → message_template_groupings.id (audience category) |
communication_id | int | FK → communication_types.id (workflow phase) |
created_at / updated_at | timestamp | Audit timestamps |
message_templates — Template Content| Column | Type | Description |
|---|---|---|
templateID | int | Primary key |
messagingID | int | FK → messaging.id (which template set) |
messageTypeID | int | FK → messaging_type.id (which trigger) |
templateType | tinyint | 1 = Email, 2 = SMS |
language_id | int | FK → languages.id |
subject | text | Email subject line (empty for SMS) |
body | longtext | HTML email body or SMS text content |
startDate | date | Scheduled start date (for time-delayed sends) |
delayDurationInDays | int | Days to wait before sending |
numberOfReminders | int | How many reminder sends to schedule |
message_template_grouping_id | int | FK → message_template_groupings.id; used for special template types (Test Results Layouts, Action Pages) |
message_template_bundle | varchar | Comma-separated bundle IDs (Test Results Layouts only) |
permit_pdf_download | tinyint | Allow PDF download of results |
permit_share_email | tinyint | Allow email sharing of results |
action_page_name | varchar | Action Pages only — display name |
url_slug | varchar | Action Pages only — URL path |
user_type | varchar | Action Pages only — user type context |
relevant_portal | varchar | Action Pages only — portal context |
redirect_url | varchar | Action Pages only — post-action redirect |
action_pages_type | varchar | Action Pages only — defaults to "Interstitial" |
custom_message | text | Optional custom message override |
description | text | Human description (Test Results Layouts) |
account_id | int | Set by global admins when a template is scoped to a specific account’s messaging set; accounts do not write this field |
newly_added_template | tinyint | Flag; highlights recently added templates in the UI |
| Table | Purpose |
|---|---|
message_template_groupings | Audience/category labels for triggers (Admins, Assignees, Observers, etc.) |
communication_types | Workflow phase labels (Ecommerce, Shipment, Test Results, etc.) |
languages | Supported languages; id=1 is English (default) |
replacement_variables | Dynamic placeholders available for use in template bodies |
email_queue | Outbound email queue; templates are sent asynchronously through this table |
communication_settings | Sandbox mode flag and sandbox redirect address |
message_template_groupingsEvery trigger is assigned to an audience grouping. These groupings appear as the “Category” filter on the admin template management page.
| ID | Name | Description |
|---|---|---|
| 1 | All Template Types | Meta-filter that shows all triggers |
| 2 | Ecommerce | Order and purchase notifications |
| 3 | Admins | Global admin system notifications |
| 4 | Managers | Shipping and workflow manager notifications |
| 5 | Accounts | Account admin notifications |
| 6 | Assignees/Patients - Notifications | Operational assignee communications |
| 7 | Assignees/Patients - Results | Test result communications to assignees |
| 8 | Observers | Observer notifications |
| 9 | Authentication | Login, 2FA, password reset |
| 10 | Test Results Layouts | Defines result page rendering (not email/SMS) |
| 11 | Employees/Groups | Employee/group account notifications |
| 12 | Providers/Medical Staff | Clinical staff notifications |
| 13 | General | Miscellaneous platform communications |
| 14 | Support Entity | Customer care and support templates |
| 15 | Shipment Inserts | Physical shipment insert templates |
| 16 | Action Pages | Interstitial pages in the test workflow (not email/SMS) |
| 17 | Direct Mail | Direct mail / physical letter templates |
| 18 | Case Management | Case management workflow communications |
communication_typesEach trigger is also classified by its position in the platform workflow. This is a higher-level grouping used to organize triggers by lifecycle stage.
| ID | Name | Triggers it covers |
|---|---|---|
| 1 | Ecommerce Transactions | Order confirmations |
| 2 | Assignee Portal Registration | New user onboarding, login credentials, self-registration |
| 3 | Shipment Communications | Kit shipped, arrived, delays, re-shipments, lab return |
| 4 | General Communications | Education reminders, observer notifications, auth, support |
| 5 | Test Results | Results available, partial results, specimen rejected, canceled |
| 6 | Direct Mail | Hand-to-patient, use-in-clinic, return labels, observer feedback |
Triggers that have no communication_id are uncategorized.
When a platform event occurs, the following steps happen to send a communication:
1. Application code identifies the trigger
└─ Hardcodes the messaging_type.id (e.g. messageTypeID = 92)
2. Look up the recipient's account → get messaging_id
└─ SELECT messaging_id FROM accounts WHERE id = ?
3. Resolve the template
└─ getMessageTemplateByMessageIdandType(messagingID, messageTypeID, languageId)
└─ Queries: message_templates WHERE messagingID=? AND messageTypeID=? AND language_id=?
4. Populate replacement variables in subject and body
└─ Substitutes dynamic placeholders with runtime values
5. Queue the email
└─ CheckCommunicationSuppression::sendEmailCM()
→ EmailQueue::addGroupMembershipUsersDataEmailQueue()
└─ Adds a row to email_queue with: to address, from, subject, body, templateId, messagingID
6. Cron: process_email_queue.php
└─ Reads unprocessed rows from email_queue
└─ Sends via PHPMailer over SMTP
└─ Logs delivery result
If step 3 finds no template, the platform calls SendEmailMessageTemplate::sendMessageTemplateNotFoundEmail(), which sends an alert email to the customer support address. The alert includes:
message_purpose name of the missing triggermessageTypeID that was looked upBefore queuing an email, CheckCommunicationSuppression::sendEmailCM() checks whether the recipient user has suppression active:
users.communication_types — sets allowed communication channel (1 = Email & SMS, 2 = Email only, 3 = SMS only)users.suppress_communication_group_id — suppresses an entire message_template_groupings categoryIf suppression is active for the relevant grouping, the email is not queued.
communication_settings contains an is_sandbox_enabled flag. When enabled, all outbound emails are redirected to the sandbox_email address rather than the actual recipient. This is used in development and staging environments.
Every template record has a language_id foreign key. A template set can have the same trigger covered in multiple languages — one template row per language. Language 1 (English) is the default and must be configured first before translations can be added.
The languages table defines available languages. A companion language_status table tracks which languages are active or selected for a given context.
The admin interface provides a Copy & Translate feature (ajaxTranslateMessageTemplate.php) that:
language_id=1) templates for a given template set and category to the selected target languagemessage_templates rows with the target language_idnewly_added_template=1, which highlights them in the template list for reviewThe Copy Missing variant does the same but only for triggers that don’t yet have a template in the target language, filling gaps without overwriting existing translations.
The workflow runs asynchronously, reporting progress as it copies and translates each template (step 1: copying, step 2: translating N of M, step 3: complete).
These templates do not send emails or SMS. Instead they define the visual layout of the test results page presented to the assignee. Key fields:
description — Human label for the layoutmessage_template_bundle — Comma-separated IDs of product bundles this layout applies topermit_pdf_download — Whether assignees can download a PDF of their resultspermit_share_email — Whether assignees can share results by emailMultiple layouts can exist in a template set, each associated with different bundles.
Action pages are interstitial screens shown to users at specific points in the test workflow. They are not emails — they are rendered as full web pages. Key fields:
action_page_name — Display name in the admin UIurl_slug — URL path fragment appended to the provisioning URLaction_pages_type — Currently always "Interstitial"user_type — Which user type sees this pagerelevant_portal — Which portal context this belongs toredirect_url — Where the user is sent after the action pageAction pages stored in the Global Library can be copied to specific accounts via the “Copy to Account” function.
Template bodies support dynamic placeholders — tokens that are substituted with real values at send time. These are stored in the replacement_variables table, scoped by type and use_type (1 = Templates).
The editor displays available variables for the template type (email vs SMS) via getReplacementVariablesShortcuts($type, 1). Variables are inserted into the subject or body and resolved by the application before the email is queued.
admin/| File | Purpose |
|---|---|
templates.php | List all template sets; filter by library (global or account-specific); add new sets |
global_messagingtemplate.php | View all templates within a selected set, filtered by category and language; add, delete, copy & translate |
edittemplate.php | Create or edit a single template (subject, body, media type, delay, reminder schedule) |
global_sitedetail.php | Edit the metadata of a template set (from addresses, URLs, logo, etc.) |
media_manager.php | Manage image/media assets used in template bodies |
ajaxTranslateMessageTemplate.php | AJAX backend for the copy-and-translate workflow |
check_missing_templates.php | Utility to identify triggers with no configured template in a given set |
account/| File | Purpose |
|---|---|
messagingtemplate.php | View the templates in the account’s assigned messaging set |
| File | Responsibility |
|---|---|
app/Models/MessagingTemplates.php | All CRUD operations on message_templates; bulk copy; replacement variable lookup; reminder email logic |
app/Models/MessagingTypes.php | Query messaging_type records; look up trigger types by language and template set; check “in use” status |
app/Models/Messaging.php | CRUD for messaging (template sets); get sets by account |
app/Helpers/CheckCommunicationSuppression.php | Checks suppression rules before queuing; calls EmailQueue to enqueue |
app/Helpers/SendEmailMessageTemplate.php | Sends the “template not found” error alert to customer support |
app/Models/EmailQueue.php | Inserts rows into the email_queue table |
When a new platform event needs to send a communication, the steps are:
Insert a row into messaging_type with the message_purpose label, an appropriate message_template_groupings_id, and communication_id.
Note the new id — this becomes the messageTypeID constant used in application code.
Author the template via the admin UI: navigate to a template set → Add Template → select the new trigger from the message purpose dropdown → write subject and body.
Reference the ID in application code wherever the event fires:
$messageTypeID = 92; // "Assignee - Kit Shipped"
$msgTemplates = $mtObj->getMessageTemplateByMessageIdandType(
$siteData['id'], $messageTypeID, $selected_language_id
);
Repeat for all affected template sets — the trigger will show as “Missing” in any set that hasn’t had a template authored for it.
Add translations as needed using Copy & Translate in each language-enabled set.