Skip to content

feat(workflows): let email waits count only the execution's own emails - #540

Open
joanproductivo wants to merge 6 commits into
useplunk:nextfrom
joanproductivo:feat/wait-for-event-execution-scope
Open

joanproductivo wants to merge 6 commits into
useplunk:nextfrom
joanproductivo:feat/wait-for-event-execution-scope

Conversation

@joanproductivo

Copy link
Copy Markdown
Contributor

Closes #537

What changed

  • WAIT_FOR_EVENT config takes an optional scope: "contact" (default) or "execution". With "execution", email.sent, email.delivery, email.open, email.click, email.bounce and email.complaint only resume the wait when the email was sent to the contact by the waiting execution; emails a step sent to a custom recipient never count. Without a scope, or for any other event, waits behave exactly as before.
  • trackEvent passes its email id to handleEvent. The email is looked up at most once per event, by primary key, and only when a scoped wait matches.
  • The custom-recipient header (X-Plunk-Recipient-Override), spelled out four times in the email service and processor, is now one constant (separate refactor commit).
  • canScopeWaitToExecution in @plunk/shared decides which events accept the scope, for both the engine and the step dialog, which shows an "Only emails from this execution" switch for those events. The MCP server's copy of the step schema accepts the field.
  • Docs: the workflows page explains the default, the scope, and that a scoped wait should have a timeout (an email that is never sent or opened would otherwise keep the execution waiting).

Verification

  • New WorkflowExecutionService.waitScope.test.ts (6 tests): unscoped waits resume on any email and look nothing up; scoped waits ignore other emails, custom-recipient sends and events without an email, resume only the execution that sent the email, and ignore the scope for other events. New workflow-events.test.ts in @plunk/shared (11 tests).
  • Full suite: 1424 tests passing (1407 + 17). tsc --noEmit clean for api, web, mcp and shared; @plunk/shared builds.
  • No schema or migration changes.

🤖 Generated with Claude Code

joanproductivo and others added 4 commits October 24, 2026 13:24
The header that carries a workflow step's custom recipient was spelled out four
times across the email service and the email processor. It is now one exported
constant next to the other email header helpers, so the workflow engine can
recognise these emails too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A WAIT_FOR_EVENT step resumes when the event is tracked on the contact, so a
wait for email.open after a SEND_EMAIL resumed on an open of any email: a
newsletter, a receipt, or a notification another step sent to a custom
recipient, which is recorded under the contact.

The step config takes an optional scope. With scope "execution", the email
events a workflow email produces (email.sent, email.delivery, email.open,
email.click, email.bounce, email.complaint) only count when the email was sent
to the contact by the waiting execution. Without a scope, or for any other
event, waits behave as before. trackEvent now hands its email id to
handleEvent, and the email is looked up once per event by primary key, only
when a scoped wait needs it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
For a wait on an email event a workflow email produces, the step dialog shows
"Only emails from this execution", which saves scope "execution". The
workflows concept page explains the default, the scope, and that emails sent to
a custom recipient never count for a scoped wait.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Resolves the two conflicts with next: email-processor.ts, where the job body
moved into processEmailJob, keeps the recipient override header as the
RECIPIENT_OVERRIDE_HEADER constant; packages/shared/src/index.ts exports both
delivery.js and workflow-events.js. handleEvent combines the scope check with
the filter on cancelled executions from useplunk#545.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
joanproductivo added a commit to joanproductivo/plunk-lite that referenced this pull request Oct 25, 2026
Merges the branch as published before it was brought up to date with upstream next, so none of next's newer changes come along. A wait on an email event can be limited to the emails its own execution sent to the contact, with the "Only emails from this execution" switch (useplunk#540).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

# Conflicts:
#	apps/api/src/services/EmailService.ts
driaug and others added 2 commits October 29, 2026 17:40
# Conflicts:
#	apps/api/src/services/WorkflowExecutionService.ts
A wait that counts only its own run's emails never resumes if that email is
never sent or opened, so the dialog now refuses to save one without a
timeout. "Execution" is our internal term; the editor says "run".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.

WAIT_FOR_EVENT on email.open / email.click resumes on engagement with any email of the contact; no way to wait for the email the workflow sent

2 participants