Skip to content

feat: add per-email tracking override to /v1/send - #433

Open
chamby wants to merge 2 commits into
useplunk:nextfrom
chamby:feat/per-email-tracking-override
Open

feat: add per-email tracking override to /v1/send#433
chamby wants to merge 2 commits into
useplunk:nextfrom
chamby:feat/per-email-tracking-override

Conversation

@chamby

@chamby chamby commented Jul 28, 2026

Copy link
Copy Markdown

Description

Adds an optional tracking boolean to POST /v1/send that overrides the project's TrackingMode for a single email. Omitted → project mode applies exactly as today; false/true → wins for that send.

Implementation follows the existing plumbing:

  • Nullable trackingOverride column on emails (+ migration) so the worker sees the override at send time
  • EmailService.sendEmail: email.trackingOverride ?? shouldTrackEmail(...) — the only behavioral change; campaigns and workflows are untouched
  • tracking: z.boolean().optional() in ActionSchemas.send, passed through Actions.sendsendTransactionalEmail
  • OpenAPI spec + tracking guide updated

Same constraint as MARKETING_ONLY: only effective when SES_CONFIGURATION_SET_NO_TRACKING is configured; when it isn't, the override is silently ignored. If you'd rather surface a warning in the response (or prefer the internal-header approach from the issue), happy to rework.

Type of Change

  • feat: New feature (MINOR version bump)

Testing

  • 7 new tests in EmailService.test.ts: override beats project mode in both directions, null falls back to project mode, persistence from sendTransactionalEmail, and schema accept/reject cases
  • Full EmailService suite passes (51 tests); yarn build --filter=api and lint clean
  • Migration validated by the test harness applying it to worker databases

Checklist

  • PR title follows conventional commits format
  • Code builds successfully
  • Tests pass locally
  • Documentation updated (if needed)

Related Issues

Closes #432


Prepared by Claude (AI assistant) acting on behalf of Matthew Chambers (@chamby), who reviewed the approach.

🤖 Generated with Claude Code

chamby and others added 2 commits July 28, 2026 14:39
Adds an optional `tracking` boolean to POST /v1/send that overrides the
project's tracking mode for a single email. Persisted as a nullable
trackingOverride column on emails; when null, the project's TrackingMode
applies as before. Campaigns and workflows are unaffected.

Like MARKETING_ONLY, the override only takes effect when
SES_CONFIGURATION_SET_NO_TRACKING is configured.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The tracking decision is duplicated in the BullMQ email processor, which
is the path /v1/send actually takes (API enqueues, worker sends). The
override was stored but never applied there. Apply it in the processor
and extract the job handler to processEmailJob so the worker send path
can be tested directly (pure extraction, no behavior change).

Found during production verification of this feature.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@chamby

chamby commented Jul 28, 2026

Copy link
Copy Markdown
Author

Heads-up on a gap found while verifying this feature against a live v0.12.0 deployment: the original diff only applied the override in EmailService.sendEmail, but the path /v1/send actually takes is API → queue → jobs/email-processor.ts, which has its own copy of the tracking decision. The override was stored but never applied — a silent no-op, confirmed by SES still emitting Open events for opted-out sends.

Pushed a fix in ab2ad0a:

  • email-processor.ts now applies email.trackingOverride ?? EmailService.shouldTrackEmail(...) (the one behavioral line)
  • Extracted the inline Worker callback to an exported processEmailJob so the worker send path is directly testable — pure extraction, no behavior change; the diff is mostly re-indentation (git diff -w shows the real changes)
  • 3 new worker-path tests covering override-wins in both directions and null-falls-back; full API suite passes (769 tests)

Might be worth unifying the two send paths at some point so decisions like this live in one place — left that alone here to keep the PR focused.

Posted by Claude on behalf of @chamby.

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.

Per-email tracking override on /v1/send

1 participant