Skip to content

fix: add replyTo and X-Formsg-Submission-ID headers to MRF outcome emails#9570

Merged
eliotlim merged 3 commits into
developfrom
fix/mrf-workflow-completion-email-headers
Jun 9, 2026
Merged

fix: add replyTo and X-Formsg-Submission-ID headers to MRF outcome emails#9570
eliotlim merged 3 commits into
developfrom
fix/mrf-workflow-completion-email-headers

Conversation

@scottheng96

@scottheng96 scottheng96 commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Problem

MRF workflow completion and approval emails were missing two email headers that storage mode admin emails include:

  1. Reply-To — without this, Zendesk automation cannot automatically assign the respondent's email as the ticket requester, requiring manual triage.
  2. X-Formsg-Submission-ID — without this, email-based integrations (e.g. Zendesk, filtering rules) cannot correlate incoming emails back to a specific FormSG submission.

Solution

Added a extractEmailAnswersFromResponses utility to multirespondent-submission.utils.ts that collects all email field answers from a FieldResponsesV3 response map (mirroring extractEmailAnswers used for storage mode). This is then joined and passed as replyTo to both sendMrfWorkflowCompletionEmail and sendMrfApprovalEmail.

Both mail service functions now also accept and forward a submissionId parameter, which #sendEmailWithTemplate uses to conditionally set the X-Formsg-Submission-ID header (the conditional was already in place — it just wasn't being passed).

Breaking Changes

  • No — this PR is backwards compatible. The new parameters are optional; existing callers not passing them will see no change in behaviour.

Tests

TC1: Workflow completion email includes correct headers

  • Set up an MRF form with at least one email field across multiple steps and complete the full workflow
  • Capture the outgoing completion email (e.g. via Mailtrap or email header inspection)
  • Verify that the Reply-To header is present and contains all email field answers from all steps, comma-separated
  • Verify that the X-Formsg-Submission-ID header is present and matches the submission ID

TC2: No email fields — headers degrade gracefully

  • Set up an MRF form with no email fields and complete the workflow
  • Verify that the completion/approval email is sent without a Reply-To header (no empty header)
  • Verify that the X-Formsg-Submission-ID header is still present

@scottheng96 scottheng96 marked this pull request as ready for review June 9, 2026 04:13
@scottheng96 scottheng96 requested a review from a team as a code owner June 9, 2026 04:13
Copilot AI review requested due to automatic review settings June 9, 2026 04:13

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates multi-respondent form (MRF) outcome emails (workflow completion + approval/rejection) to include the same integration-critical headers as storage-mode admin emails, by wiring through Reply-To and X-Formsg-Submission-ID.

Changes:

  • Added an MRF utility to extract all email-field answers from a FieldResponsesV3 map for use as a Reply-To value.
  • Updated MRF outcome email send paths to pass replyTo and submissionId into the mail template sender, enabling Reply-To and X-Formsg-Submission-ID headers.
  • Normalised formId passed into these MRF outcome mail calls to a string.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

File Description
apps/backend/src/app/services/mail/mail.service.ts Extends MRF outcome email methods to accept/forward replyTo and submissionId into the templated mail sender (enabling Reply-To + X-Formsg-Submission-ID).
apps/backend/src/app/modules/submission/multirespondent-submission/multirespondent-submission.utils.ts Adds extractEmailAnswersFromResponses to collect email answers from MRF response maps.
apps/backend/src/app/modules/submission/multirespondent-submission/multirespondent-submission.service.ts Passes extracted email answers as replyTo and wires submissionId into MRF outcome email sends.

Comment on lines +112 to +116
export const extractEmailAnswersFromResponses = (
responses: FieldResponsesV3,
): string[] => {
if (!responses) return []
return Object.values(responses)
Comment on lines 1217 to +1221
attachments,
emailType: EmailType.WorkflowCompletion,
actionName: 'sendMrfWorkflowCompletionEmail',
submissionId,
replyTo,
Comment on lines 1265 to +1269
attachments,
emailType: EmailType.WorkflowApproval,
actionName: 'sendMrfApprovalEmail',
submissionId,
replyTo,

@eliotlim eliotlim left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for fixing this, @scottheng96!

  • tested on stg-alt, no issues -- even if a subsequent step clears the email field used for Reply-To
  • we should follow up with the formId / submissionId for other email types too in a separate PR.

@eliotlim eliotlim merged commit b7e9c32 into develop Jun 9, 2026
27 checks passed
@eliotlim eliotlim deleted the fix/mrf-workflow-completion-email-headers branch June 9, 2026 04:29
@eliotlim eliotlim mentioned this pull request Jun 9, 2026
10 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants