Skip to content

fix(worker): populate email-webhook payloadDetails content for v2 workflows fixes NV-8153#11743

Open
scopsy wants to merge 2 commits into
nextfrom
cursor/fix-email-webhook-content-41f5
Open

fix(worker): populate email-webhook payloadDetails content for v2 workflows fixes NV-8153#11743
scopsy wants to merge 2 commits into
nextfrom
cursor/fix-email-webhook-content-41f5

Conversation

@scopsy

@scopsy scopsy commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes empty content in email-webhook payloadDetails when sending emails from new dashboard (v2) workflows, while preserving legacy v0 payload behavior.

Problem

New dashboard workflows store email body in control values and render it via the bridge/framework path. The message template's content field is intentionally empty (''), but SendMessageEmail was passing that raw template value into payloadDetails.content for the email-webhook provider.

The old dashboard populated content because template compilation mutated the in-memory blocks array referenced by payload.content. Bridge-based v2 workflows skip that compilation path, leaving content as an empty string while subject and rendered HTML were correct.

Solution

For email-webhook integrations, populate payloadDetails.content only when bridge output is present (v2 workflows):

if (command.bridgeData) {
  payload.content = (bridgeOutputs as EmailOutput)?.body || html || '';
}

Legacy v0 workflows without bridge output keep the existing payloadDetails subject/content contract unchanged.

Test plan

  • Worker spec: v2 bridge workflow populates payloadDetails.content with rendered HTML
  • Worker spec: legacy v0 workflow preserves template payloadDetails.content and subject
  • Trigger a v2 workflow email step with email-webhook integration and verify payloadDetails.content contains rendered HTML

Linear Issue: NV-8153

Open in Web Open in Cursor 

Greptile Summary

This PR fixes an empty payloadDetails.content field in email-webhook integrations when emails are sent through v2 (new-dashboard) workflows. The root cause was that v2 workflows store the rendered body in bridge outputs and skip template compilation, leaving payload.content as the raw (empty) template string.

  • send-message-email.usecase.ts: Adds a guard inside the EmailWebhook branch — when command.bridgeData is present the handler overwrites payload.content with bridgeOutputs.body (falling back to the compiled html variable and then ''), before assigning payload to mailData.payloadDetails. V0 workflows without bridgeData are unaffected.
  • send-message-email.usecase.spec.ts: New spec file with two focused test cases: one verifying that a v2 workflow sets payloadDetails.content to the rendered bridge body, and one verifying that a v0 workflow preserves the raw template string and subject unchanged.

Confidence Score: 5/5

Safe to merge — the change is narrowly scoped to the EmailWebhook integration path and only activates when bridge data is present, leaving all other send paths untouched.

The fix is minimal and well-contained: four lines inside a doubly-guarded block (EmailWebhook provider AND bridgeData present). The fallback chain mirrors the identical pattern already used on line 356 for mailData.html. Unit tests cover both the v2 and v0 paths, and the constructor argument count in the test matches the 13-parameter signature exactly.

No files require special attention.

Important Files Changed

Filename Overview
apps/worker/src/app/workflow/usecases/send-message/send-message-email.usecase.ts Adds four lines to populate payloadDetails.content from bridge outputs (or html) when command.bridgeData is present, fixing empty content for v2 email-webhook workflows while leaving the v0 path untouched.
apps/worker/src/app/workflow/usecases/send-message/send-message-email.usecase.spec.ts New unit-test file covering the v2 bridge-workflow case (content populated from bridge body) and the v0 legacy case (content preserved as raw template string); constructor argument count matches the 13-parameter signature.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[SendMessageEmail.execute] --> B{command.bridgeData?}
    B -- Yes v2 workflow --> C[Skip compileEmailTemplateUsecase]
    B -- No v0 workflow --> D[compileEmailTemplateUsecase.execute]
    C --> E[Build payload with empty content]
    D --> E
    E --> F[Build mailData]
    F --> G{EmailWebhook?}
    G -- No --> H[sendMessage]
    G -- Yes --> I{command.bridgeData?}
    I -- Yes v2 --> J[payload.content = bridgeOutputs.body or html or '']
    I -- No v0 --> K[payload.content stays as raw template string]
    J --> L[mailData.payloadDetails = payload]
    K --> L
    L --> H
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A[SendMessageEmail.execute] --> B{command.bridgeData?}
    B -- Yes v2 workflow --> C[Skip compileEmailTemplateUsecase]
    B -- No v0 workflow --> D[compileEmailTemplateUsecase.execute]
    C --> E[Build payload with empty content]
    D --> E
    E --> F[Build mailData]
    F --> G{EmailWebhook?}
    G -- No --> H[sendMessage]
    G -- Yes --> I{command.bridgeData?}
    I -- Yes v2 --> J[payload.content = bridgeOutputs.body or html or '']
    I -- No v0 --> K[payload.content stays as raw template string]
    J --> L[mailData.payloadDetails = payload]
    K --> L
    L --> H
Loading

Comments Outside Diff (1)

  1. General comment

    P1 Legacy email-webhook payloadDetails subject/content changed from pre-change behavior

    • Bug
      • The requested contract says legacy/v1 email-webhook payloads should remain behaviorally unchanged. The before/after run shows base legacy payloadDetails carried the template payload values (Template subject {{name}}, <p>Template legacy {{name}}</p>), while head rewrites them to the final compiled subject/content (Final legacy compiled subject for Grace, <p>Compiled legacy HTML for Grace</p>). This is a user-visible provider request payloadDetails contract change for legacy email-webhook integrations.
    • Cause
      • The new email-webhook branch in apps/worker/src/app/workflow/usecases/send-message/send-message-email.usecase.ts unconditionally assigns payload.subject = subject and, when command.bridgeData is absent, assigns payload.content = (content ?? html ?? step.template.content). That code path also runs for legacy/v1 email-webhook sends, not just bridge/v2 sends.
    • Fix
      • If legacy/v1 must remain unchanged, scope the payloadDetails subject/content rewrites to bridge/v2 workflows only, or preserve the previous payload object for non-bridge sends. If compiled subject/content is intentionally desired for legacy too, update the contract/request because the executed behavior is not unchanged.

    T-Rex Ran code and verified through T-Rex

Reviews (2): Last reviewed commit: "fix(worker): scope email-webhook payload..." | Re-trigger Greptile

…kflows fixes NV-8153

Co-authored-by: Dima Grossman <dima@grossman.io>
@linear-code

linear-code Bot commented Jun 30, 2026

Copy link
Copy Markdown

NV-8153

@netlify

netlify Bot commented Jun 30, 2026

Copy link
Copy Markdown

Deploy preview added

Name Link
🔨 Latest commit a4c35d0
🔍 Latest deploy log https://app.netlify.com/projects/dashboard-v2-novu-staging/deploys/6a44cf8a3f7ee600085a8de1
😎 Deploy Preview https://deploy-preview-11743.dashboard-v2.novu-staging.co
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@scopsy

scopsy commented Jun 30, 2026

Copy link
Copy Markdown
Contributor Author

@cursoragent let's introduce an e2e test to verify our fix

@cursor

cursor Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Taking a look!

Open in Web Open in Cursor 

Comment on lines +376 to +379
payload.subject = subject;
payload.content = command.bridgeData
? (bridgeOutputs as EmailOutput)?.body || html || ''
: (content ?? html ?? step.template.content);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Let's only do that for v2 workflows where bridge output is present to ensure backwards compatability for v0 templates @cursoragent

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@cursoragent Let's only do that for v2 workflows where bridge output is present to ensure backwards compatability for v0 templates

@cursor cursor Bot Jul 1, 2026

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.

Scoping is updated per review: payloadDetails.content is only rewritten when bridge output is present (v2 workflows). Legacy v0 email-webhook payloads are unchanged.

    if (integration.providerId === EmailProviderIdEnum.EmailWebhook) {
      if (command.bridgeData) {
        payload.content = (bridgeOutputs as EmailOutput)?.body || html || '';
      }

      mailData.payloadDetails = payload;
    }

What changed

  • Removed the legacy fallback (content ?? html ?? step.template.content) and the unconditional payload.subject = subject update.
  • For v0 templates, payloadDetails still carries the original template subject/content (with the existing in-place editor compilation behavior).
  • For v2 bridge workflows, payloadDetails.content is set from the rendered bridge email body.

Tests added in send-message-email.usecase.spec.ts:

  • v2 bridge workflow → payloadDetails.content is populated with rendered HTML
  • v0 legacy workflow → payloadDetails.content and subject stay as template values

Both worker specs pass. Changes are pushed to PR #11743.

Open in Web Open in Cursor 

…kflows fixes NV-8153

Co-authored-by: Dima Grossman <dima@grossman.io>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants