Skip to content

feat(messaging): store a sender and reply-to on transactional templates - #121871

Merged
trunk-io[bot] merged 2 commits into
masterfrom
posthog/transactional-email-sender-api
Oct 22, 2026
Merged

trunk-io[bot] merged 2 commits into
masterfrom
posthog/transactional-email-sender-api

Conversation

@mayteio

@mayteio mayteio commented Oct 22, 2026

Copy link
Copy Markdown
Contributor

Problem

  • A transactional email can't choose who it comes from. Every template sends from the sender picked when it was made transactional, with no reply-to.
  • Moving PostHog's own emails off Customer.io needs this. A security email should come from a security address and take replies there.
  • The send path also accepts any text as a custom sender address, then quietly sends from the sender's own address instead.

This is the API half of the feature. The editor is #121079, stacked on this PR.

Changes

  • Template content takes from (sender, custom name and address) and replyTo. The API and the MCP email template tools accept both.
  • Making a template transactional stores the sender it was made with. Each publish copies the sender, name, address and reply-to into the hidden workflow, so the sender can change after conversion.
  • A custom address must be one address, at most 254 characters, on the sender's verified domain. The API rejects any other address on publish. Conversion rejects an address left over from a sender on another domain. Drafts still save, and a Liquid address is checked only at send time, as today.
  • A string or empty from means no sender, on reads and writes. That covers blank templates, templates saved before this change, and templates saved from a destination's email editor.
  • A sender without an integration ID is a 400.
  • Mechanical: regenerated frontend types and MCP schemas and snapshots, and a facade function that resolves the default sender.

How did you test this code?

The full feature was tested manually on a local stack, with the editor from #121079. A transactional template got a custom sender, display name, address and reply-to, published, and was sent through the API. MailDev received From: Hedgebox Security Team <security-team@posthog.com> and Reply-To: security@posthog.com.

Test rationale:

  • test_saving_a_transactional_template_writes_a_new_workflow_version switches the sender and sets a name, a Liquid address and a reply-to. It fails if the serializer drops a field or the sync ignores one.
  • test_rejects_an_update_that_would_stop_a_transactional_template_sending gains these cases: another project's sender, an unverified sender, an off-domain address, a non-address, an address that is too long, and a sender without an ID.
  • test_rejects_making_a_template_transactional_it_cannot_send gains a custom address left on another sender's domain.
  • test_retrieve_message_template lists and reads a template stored with each legacy from shape. Each one crashed the list before.
  • test_create_email_template_with_subject_succeeds creates with the editor's blank from and with a destination's sender string.
  • Every new case fails without this change.

Release status

  • This change is behind a feature flag and is not available to users

Transactional templates sit behind workflows-transactional-email-api.

Docs update

None. No existing doc covers transactional template settings.

🤖 Agent context

Autonomy: Human-driven (agent-assisted)

Agent: PostHog Desktop with Claude Code, Claude Opus 5.5 (claude-opus-5-5)


Created with PostHog Desktop

🤖 Generated with Claude Code

- Template content takes `from` (sender, custom name and address) and `replyTo`. Each publish copies them into the transactional workflow, and making a template transactional stores the sender it was made with.
- A custom address must be one address of at most 254 characters on the sender's verified domain. The API rejects it on publish, and conversion rejects one left over from another sender.
- A sender string or empty value, from blank templates, older templates or a destination's editor, means no sender, on reads and writes.
- A sender without an integration ID is a 400.

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

Generated-By: PostHog Desktop
Task-Id: 90d9a51d-beba-47ba-8f50-b351327676f9
@mayteio mayteio self-assigned this Oct 22, 2026
@trunk-io

trunk-io Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

😎 This pull request was merged.

@github-actions

github-actions Bot commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

🤖 CI report

⚠️ Trunk lane — backend Python lane

This PR is assigned to the backend Python lane. It runs backend Python tests and may merge in parallel with PRs in other lanes.

✅ Complexity (TypeScript) — clean

Cyclomatic complexity above the limit in changed typescript files (10 for production files, 15 for test files). Warn only: worth simplifying when you next touch these functions.

✅ Duplication (Python) — clean

New Python code duplication introduced by this branch. Fails at 70+ tokens in app code, or 150+ tokens when both copies live in test files. Advisory while the gate proves itself: extract a shared helper instead of copying.

✅ Duplication (TypeScript) — clean

New TypeScript code duplication introduced by this branch. Fails at 70+ tokens in app code, or 150+ tokens when both copies live in test files. Advisory while the gate proves itself: extract a shared helper instead of copying.

🚨 Comment density — 7% of added code lines are comments (18 of 241)

This section warns when comments are more than 3% of the code lines a PR adds, and alerts above 6%. Before agent-assisted PRs, the typical share was about 2%. Only full-line comments count. Docstrings, generated files, snapshots, migrations, and workflow files are left out.

Comments that restate the code, record how the change came about, or narrate the next line add noise for the next reader. Keep the comments that explain a reason the code cannot show, and remove the rest. See .agents/skills/writing-code-comments/SKILL.md for the house rules.

Files with the most added comment lines:

File Comment lines Added lines
products/messaging/backend/services/transactional_email.py 8 53
products/messaging/backend/presentation/views/message_templates.py 6 63
products/messaging/backend/tests/api/test_message_templates.py 2 26
products/messaging/backend/tests/api/test_transactional_email.py 1 78
products/workflows/backend/facade/contracts.py 1 4

This check does not block merging. It updates on every push and clears when the share drops.

✅ Bundle size — no change

Uncompressed size of every built .js bundle, compared against the base branch.

Total: 75.83 MiB · no change

No file changed by more than 1000 B.

Posted automatically by build-bundle-size-report · uncompressed bytes from dist-report

⚠️ Eager graph — 2 over budget

How much code each root ships on the eager path — downloaded and parsed before the surface is interactive. Measured from the esbuild output chunks (post-tree-shake, static imports only); lazy import() / React.lazy chunks are not counted.

Root Eager (shipped) Δ vs base Budget
entry (logged-out pages, app bootstrap)
src/index.tsx
1.01 MiB · 24 files no change █████████░ 88.6% of 1.14 MiB
logged-out boot: index + App + bootApp (preloaded by every page, including /login)
src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
2.93 MiB · 615 files no change ██████████ 96.8% of 3.03 MiB
🟡 authenticated shell (every logged-in page)
src/scenes/AuthenticatedShell.tsx
7.59 MiB · 2,580 files no change ██████████ 101.2% of 7.50 MiB
dashboard scene
src/scenes/dashboard/Dashboard.tsx
9.61 MiB · 3,646 files no change ██████████ 96.0% of 10.01 MiB
🟡 today home path
src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/today/TodayHome.tsx
7.99 MiB · 2,919 files no change ██████████ 103.3% of 7.74 MiB
events scene
src/scenes/activity/explore/EventsScene.tsx
9.09 MiB · 3,437 files no change █████████░ 94.8% of 9.59 MiB
web analytics scene
src/scenes/web-analytics/WebAnalyticsScene.tsx
9.93 MiB · 3,652 files no change █████████░ 91.7% of 10.83 MiB
replay detail scene
src/scenes/session-recordings/detail/SessionRecordingDetail.tsx
8.16 MiB · 3,021 files no change █████████░ 86.2% of 9.47 MiB

🟢 node_modules/monaco-editor/ stays out of src/index.tsx
🟢 src/lib/components/ActivityLog/describers stays out of src/index.tsx
🟢 node_modules/@posthog/brand/dist/generated/hoggies/svg/ stays out of src/index.tsx
🟢 node_modules/@posthog/brand/dist/generated/hoggies/components/ stays out of src/index.tsx
🟢 node_modules/monaco-editor/ stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/layout/navigation-3000/navigationLogic.tsx stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/scenes/dashboard/dashboardLogic.tsx stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/lib/lemon-ui/LemonMarkdown/ stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/lib/components/RichContentEditor/ stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/lib/components/CodeSnippet/ stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/taxonomy/core-filter-definitions-by-group.json stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 node_modules/zod/ stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/scenes/billing/billingLogic.tsx stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/lib/components/ProductEmptyState/productSetupPreloadLogic.ts stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 node_modules/monaco-editor/ stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/lib/components/ActivityLog/describers stays out of src/scenes/AuthenticatedShell.tsx
🟢 products/dashboards/frontend/widgets/previews/ stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/scenes/session-recordings/player/sessionRecordingPlayerLogic.ts stays out of src/scenes/AuthenticatedShell.tsx
🟢 products/wizard/frontend/runs/WizardRunDetailsDrawer.tsx stays out of src/scenes/AuthenticatedShell.tsx
🟢 zod/v4/locales/de.js stays out of src/scenes/AuthenticatedShell.tsx
🟢 node_modules/@posthog/brand/dist/generated/hoggies/svg/ stays out of src/scenes/AuthenticatedShell.tsx
🟢 node_modules/@posthog/brand/dist/generated/hoggies/components/ stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/scenes/session-recordings/playlist/SessionRecordingsPlaylist.tsx stays out of src/scenes/dashboard/Dashboard.tsx
🟢 src/scenes/web-analytics/tiles/WebAnalyticsTile.tsx stays out of src/scenes/dashboard/Dashboard.tsx
🟢 src/queries/validators.js stays out of src/scenes/dashboard/Dashboard.tsx
🟢 src/scenes/project-homepage/ai-first/AiFirstHomepage.tsx stays out of src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/today/TodayHome.tsx
🟢 src/scenes/project-homepage/today/TodayReportPage.tsx stays out of src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/today/TodayHome.tsx
🟢 src/queries/Query/Query.tsx stays out of src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/today/TodayHome.tsx
🟢 src/scenes/session-recordings/playlist/SessionRecordingsPlaylist.tsx stays out of src/scenes/activity/explore/EventsScene.tsx
🟢 src/scenes/web-analytics/tiles/WebAnalyticsTile.tsx stays out of src/scenes/activity/explore/EventsScene.tsx
🟢 src/queries/validators.js stays out of src/scenes/activity/explore/EventsScene.tsx
🟢 src/scenes/web-analytics/LiveMetricsDashboard/LiveWebAnalyticsMetrics.tsx stays out of src/scenes/web-analytics/WebAnalyticsScene.tsx
🟢 products/product_analytics/frontend/insights/trends/WorldMap/countryVectors.tsx stays out of src/scenes/web-analytics/WebAnalyticsScene.tsx
🟢 src/scenes/settings/SettingsMap.tsx stays out of src/scenes/session-recordings/detail/SessionRecordingDetail.tsx
🟢 @jspm/core/nodelibs/browser/fs stays out of src/scenes/session-recordings/detail/SessionRecordingDetail.tsx

Largest files eagerly shipped from src/index.tsx
Size File
126.8 KiB ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js
24.6 KiB ../node_modules/.pnpm/buffer@6.0.3/node_modules/buffer/index.js
6.3 KiB ../node_modules/.pnpm/react@18.3.1/node_modules/react/cjs/react.production.min.js
4.5 KiB ../node_modules/.pnpm/@jspm+core@2.1.0/node_modules/@jspm/core/nodelibs/browser/process.js
3.9 KiB ../node_modules/.pnpm/scheduler@0.23.2/node_modules/scheduler/cjs/scheduler.production.min.js
1.4 KiB ../node_modules/.pnpm/base64-js@1.5.1/node_modules/base64-js/index.js
1.4 KiB src/index.tsx
1.3 KiB src/RootErrorBoundary.tsx
1.3 KiB src/lib/utils/bootTiming.ts
912 B ../node_modules/.pnpm/ieee754@1.2.1/node_modules/ieee754/index.js
Largest files eagerly shipped from src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
Size File
321.6 KiB ../node_modules/.pnpm/posthog-js@1.438.12_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
220.9 KiB ../node_modules/.pnpm/@posthog+icons@0.38.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js
126.8 KiB ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js
106.8 KiB src/products.tsx
98.3 KiB src/lib/api.ts
69.4 KiB src/lib/lemon-ui/icons/icons.tsx
38.7 KiB ../node_modules/.pnpm/@dnd-kit+core@6.0.8_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@dnd-kit/core/dist/core.esm.js
33.9 KiB ../node_modules/.pnpm/kea@4.0.0-pre.6_patch_hash=139b8d1f1304f9d9da452a9a1244c94ea679dbcb85687d8999563146879fb6f5_react@18.3.1/node_modules/kea/lib/index.cjs.js
29.4 KiB src/scenes/scenes.ts
26.8 KiB src/lib/constants.tsx
Largest files eagerly shipped from src/scenes/AuthenticatedShell.tsx
Size File
321.6 KiB ../node_modules/.pnpm/posthog-js@1.438.12_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
280.7 KiB src/taxonomy/core-filter-definitions-by-group.json
238.5 KiB ../packages/quill/packages/quill/dist/index.js
220.9 KiB ../node_modules/.pnpm/@posthog+icons@0.38.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js
153.7 KiB ../node_modules/.pnpm/re2js@0.4.1/node_modules/re2js/build/index.esm.js
143.2 KiB ../packages/quill/packages/primitives/dist/index.js
126.8 KiB ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js
106.8 KiB src/products.tsx
98.3 KiB src/lib/api.ts
93.3 KiB ../node_modules/.pnpm/prosemirror-view@1.40.1/node_modules/prosemirror-view/dist/index.js
Largest files eagerly shipped from src/scenes/dashboard/Dashboard.tsx
Size File
321.6 KiB ../node_modules/.pnpm/posthog-js@1.438.12_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
280.7 KiB src/taxonomy/core-filter-definitions-by-group.json
238.5 KiB ../packages/quill/packages/quill/dist/index.js
220.9 KiB ../node_modules/.pnpm/@posthog+icons@0.38.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js
153.7 KiB ../node_modules/.pnpm/re2js@0.4.1/node_modules/re2js/build/index.esm.js
143.2 KiB ../packages/quill/packages/primitives/dist/index.js
126.8 KiB ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js
106.8 KiB src/products.tsx
98.3 KiB src/lib/api.ts
93.3 KiB ../node_modules/.pnpm/prosemirror-view@1.40.1/node_modules/prosemirror-view/dist/index.js
Largest files eagerly shipped from src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/today/TodayHome.tsx
Size File
321.6 KiB ../node_modules/.pnpm/posthog-js@1.438.12_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
280.7 KiB src/taxonomy/core-filter-definitions-by-group.json
238.5 KiB ../packages/quill/packages/quill/dist/index.js
220.9 KiB ../node_modules/.pnpm/@posthog+icons@0.38.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js
153.7 KiB ../node_modules/.pnpm/re2js@0.4.1/node_modules/re2js/build/index.esm.js
143.2 KiB ../packages/quill/packages/primitives/dist/index.js
126.8 KiB ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js
106.8 KiB src/products.tsx
98.3 KiB src/lib/api.ts
93.3 KiB ../node_modules/.pnpm/prosemirror-view@1.40.1/node_modules/prosemirror-view/dist/index.js
Largest files eagerly shipped from src/scenes/activity/explore/EventsScene.tsx
Size File
321.6 KiB ../node_modules/.pnpm/posthog-js@1.438.12_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
280.7 KiB src/taxonomy/core-filter-definitions-by-group.json
238.5 KiB ../packages/quill/packages/quill/dist/index.js
220.9 KiB ../node_modules/.pnpm/@posthog+icons@0.38.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js
153.7 KiB ../node_modules/.pnpm/re2js@0.4.1/node_modules/re2js/build/index.esm.js
143.2 KiB ../packages/quill/packages/primitives/dist/index.js
126.8 KiB ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js
106.8 KiB src/products.tsx
98.3 KiB src/lib/api.ts
93.3 KiB ../node_modules/.pnpm/prosemirror-view@1.40.1/node_modules/prosemirror-view/dist/index.js
Largest files eagerly shipped from src/scenes/web-analytics/WebAnalyticsScene.tsx
Size File
321.6 KiB ../node_modules/.pnpm/posthog-js@1.438.12_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
280.7 KiB src/taxonomy/core-filter-definitions-by-group.json
238.5 KiB ../packages/quill/packages/quill/dist/index.js
220.9 KiB ../node_modules/.pnpm/@posthog+icons@0.38.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js
183.2 KiB src/queries/validators.js
153.7 KiB ../node_modules/.pnpm/re2js@0.4.1/node_modules/re2js/build/index.esm.js
143.2 KiB ../packages/quill/packages/primitives/dist/index.js
126.8 KiB ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js
106.8 KiB src/products.tsx
98.3 KiB src/lib/api.ts
Largest files eagerly shipped from src/scenes/session-recordings/detail/SessionRecordingDetail.tsx
Size File
321.6 KiB ../node_modules/.pnpm/posthog-js@1.438.12_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
315.9 KiB ../node_modules/.pnpm/posthog-js@1.438.12_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/rrweb.js
280.7 KiB src/taxonomy/core-filter-definitions-by-group.json
238.5 KiB ../packages/quill/packages/quill/dist/index.js
220.9 KiB ../node_modules/.pnpm/@posthog+icons@0.38.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js
153.7 KiB ../node_modules/.pnpm/re2js@0.4.1/node_modules/re2js/build/index.esm.js
126.8 KiB ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js
106.8 KiB src/products.tsx
98.3 KiB src/lib/api.ts
93.3 KiB ../node_modules/.pnpm/prosemirror-view@1.40.1/node_modules/prosemirror-view/dist/index.js

Posted automatically by check-eager-graph · sizes are eager output bytes (shipped, post-tree-shake) from the esbuild metafile · part of #32479

✅ Toolbar bundle — eager 2.28 MiB within budget

What the toolbar ships to customer pages, measured from the esbuild output (minified, post-tree-shake). The eager set is the entry plus everything statically imported from it — fetched before any feature runs; deferred chunks load lazily. The eager guardrail is 5.72 MiB. Each output file must also stay below 10 MB, where CloudFront stops compressing it. The module boundary is enforced separately by check-toolbar-graph.

Metric Size Δ vs base Budget
Eager (shipped)
entry + static imports
2.28 MiB · 19 files no change ████░░░░░░ 39.9% of 5.72 MiB
Deferred (lazy) 2.10 MiB · 40 files no change n/a — loads on demand
Loader dist/toolbar.js 1.2 KiB no change █░░░░░░░░░ 6.0% of 19.5 KiB
Largest eagerly-shipped chunks
Size File
898.5 KiB dist/toolbar/toolbar-app-LIRVHRPD.css
691.0 KiB dist/toolbar/chunk-chunk-HVEQZXL4.js
258.3 KiB dist/toolbar/chunk-chunk-YUTUHJHZ.js
131.8 KiB dist/toolbar/chunk-chunk-FDH2IBXT.js
130.4 KiB dist/toolbar/chunk-chunk-MGILAAF6.js
74.8 KiB dist/toolbar/toolbar-app-F7Q25OJS.js
69.0 KiB dist/toolbar/chunk-chunk-TSAL54PB.js
35.6 KiB dist/toolbar/chunk-chunk-CWKXICRU.js
21.7 KiB dist/toolbar/chunk-chunk-XK3VNHDG.js
6.8 KiB dist/toolbar/chunk-chunk-DV7IWQNF.js

Posted automatically by check-toolbar-size · sizes are toolbar output bytes (shipped, post-tree-shake) from the esbuild metafile

✅ Dist folder size — no change

Total size of the built frontend/dist folder (all assets), compared against the base branch.

Total: 1016.05 MiB · no change

ℹ️ MCP UI apps size — 34 app(s), 27836.6 KB JS

Built size of each MCP UI app (main.js + styles.css).

App JS CSS
debug 840.2 KB 209.0 KB
action 764.3 KB 209.0 KB
action-list 831.2 KB 209.0 KB
cohort 763.3 KB 209.0 KB
cohort-list 830.2 KB 209.0 KB
email-template 763.1 KB 209.0 KB
error-details 768.1 KB 209.0 KB
error-issue 764.6 KB 209.0 KB
error-issue-list 831.8 KB 209.0 KB
experiment 828.3 KB 209.0 KB
experiment-list 832.0 KB 209.0 KB
experiment-results 830.8 KB 209.0 KB
feature-flag 830.6 KB 209.0 KB
feature-flag-list 834.3 KB 209.0 KB
feature-flag-testing 766.5 KB 209.0 KB
inline-scan 763.7 KB 209.0 KB
insight-actors 829.7 KB 209.0 KB
llm-costs 827.0 KB 209.0 KB
session-recording 765.5 KB 209.0 KB
survey 764.9 KB 209.0 KB
survey-global-stats 827.0 KB 209.0 KB
survey-list 831.9 KB 209.0 KB
survey-stats 827.0 KB 209.0 KB
trace-span 763.6 KB 209.0 KB
trace-span-list 831.1 KB 209.0 KB
vision-observation-list 830.3 KB 209.0 KB
workflow 763.6 KB 209.0 KB
workflow-list 830.6 KB 209.0 KB
workflow-setup-status 763.3 KB 209.0 KB
query-results 1082.2 KB 209.0 KB
render-ui 1157.0 KB 209.0 KB
visual-review-snapshots 768.1 KB 209.0 KB
workflow-data-suggestions 765.9 KB 209.0 KB
workflow-sending-rules 764.6 KB 209.0 KB
ℹ️ MCP agent API — agent-facing tool changes

What this PR changes for agents, from the tool schema snapshots and tool definitions.

Tools changed (2):

Tool Params Scopes Annotations Schema chars
workflows-create-email-template 3,354 -> 4,347
workflows-update-email-template 3,440 -> 4,433
✅ Playwright — all passed

All tests passed.

View test results →

⚠️ Backend coverage — 97.0% of changed backend lines covered — 3 uncovered

🧪 Backend test coverage

Patch coverage — changed backend lines (products + core): ███████████████████░ 97.0% (113 / 116)

File Patch Uncovered changed lines
products/messaging/backend/services/transactional_email.py 90.6% 79, 85, 95

🤖 Agents: add a test only if an uncovered line exposes a realistic regression that existing tests miss. Otherwise explain why no new test is needed under "How did you test this code?". Gap list: the patch-coverage artifact on this run (gh run download 502843202383296 -n patch-coverage), or the coverage-data block at the end of this comment.

Per-product line coverage (touched products)
Product Coverage Lines
demo ███████████░░░░░░░░░ 56.4% 1,542 / 2,736
batch_exports ████████████████░░░░ 81.6% 22,832 / 27,996
engineering_analytics █████████████████░░░ 85.0% 11,463 / 13,482
commands █████████████████░░░ 85.5% 1,618 / 1,893
mcp_analytics █████████████████░░░ 86.1% 5,699 / 6,617
warehouse_suggestions ██████████████████░░ 87.7% 2,422 / 2,762
posthog_ai ██████████████████░░ 87.9% 4,287 / 4,875
dashboards ██████████████████░░ 88.3% 7,327 / 8,300
notebooks ██████████████████░░ 88.5% 15,287 / 17,281
mcp_registry ██████████████████░░ 89.0% 1,596 / 1,794
cohorts ██████████████████░░ 89.6% 8,568 / 9,559
product_tours ██████████████████░░ 89.7% 1,355 / 1,510
signals ██████████████████░░ 90.6% 80,008 / 88,328
tasks ██████████████████░░ 90.7% 79,609 / 87,743
data_warehouse ██████████████████░░ 90.8% 14,722 / 16,216
canvas ██████████████████░░ 91.1% 7,998 / 8,776
today ██████████████████░░ 91.2% 3,798 / 4,163
cdp ██████████████████░░ 91.4% 10,166 / 11,119
autoresearch ██████████████████░░ 91.4% 11,606 / 12,693
slack_app ██████████████████░░ 91.6% 20,010 / 21,852
metrics ██████████████████░░ 91.6% 10,231 / 11,171
business_knowledge ██████████████████░░ 91.6% 9,965 / 10,878
data_modeling ██████████████████░░ 91.7% 10,072 / 10,985
wizard ██████████████████░░ 91.7% 6,870 / 7,490
managed_warehouse ██████████████████░░ 91.8% 11,923 / 12,995
error_tracking ██████████████████░░ 91.9% 17,572 / 19,120
streamlit_apps ██████████████████░░ 91.9% 3,139 / 3,415
conversations ██████████████████░░ 92.2% 30,218 / 32,778
review_hog ██████████████████░░ 92.2% 17,726 / 19,221
stamphog ███████████████████░ 92.5% 8,776 / 9,483
early_access_features ███████████████████░ 92.6% 1,339 / 1,446
alerts_platform ███████████████████░ 92.8% 7,386 / 7,962
notifications ███████████████████░ 93.0% 1,199 / 1,289
web_analytics ███████████████████░ 93.2% 25,607 / 27,465
alerts ███████████████████░ 93.3% 9,442 / 10,118
surveys ███████████████████░ 93.4% 6,918 / 7,406
cross_project_dashboards ███████████████████░ 93.4% 880 / 942
approvals ███████████████████░ 93.4% 4,576 / 4,898
exports ███████████████████░ 93.6% 4,125 / 4,406
billing_alerts ███████████████████░ 93.7% 2,093 / 2,233
context_layer ███████████████████░ 93.8% 3,946 / 4,208
messaging ███████████████████░ 93.8% 4,692 / 5,002
mcp_store ███████████████████░ 93.9% 9,084 / 9,676
marketing_analytics ███████████████████░ 93.9% 21,083 / 22,454
customer_analytics ███████████████████░ 94.0% 27,699 / 29,453
workflows ███████████████████░ 94.1% 22,330 / 23,719
ai_observability ███████████████████░ 94.2% 27,744 / 29,447
logs ███████████████████░ 94.3% 19,216 / 20,369
replay_vision ███████████████████░ 94.5% 31,999 / 33,861
actions ███████████████████░ 94.7% 1,004 / 1,060
reminders ███████████████████░ 94.8% 760 / 802
growth ███████████████████░ 94.9% 11,347 / 11,959
endpoints ███████████████████░ 95.1% 9,658 / 10,160
annotations ███████████████████░ 95.1% 1,095 / 1,151
skills ███████████████████░ 95.2% 7,336 / 7,707
experiments ███████████████████░ 95.3% 38,842 / 40,739
access_control ███████████████████░ 95.5% 8,079 / 8,459
data_catalog ███████████████████░ 95.6% 4,513 / 4,721
product_analytics ███████████████████░ 95.8% 29,661 / 30,966
revenue_analytics ███████████████████░ 95.9% 1,877 / 1,958
data_quality ███████████████████░ 96.0% 8,523 / 8,878
feature_flags ███████████████████░ 96.2% 27,805 / 28,914
subscriptions ███████████████████░ 96.3% 12,031 / 12,494
tracing ███████████████████░ 96.4% 4,360 / 4,524
warehouse_sources ███████████████████░ 96.5% 467,816 / 484,734
account_audits ███████████████████░ 96.7% 768 / 794
pulse ███████████████████░ 97.1% 2,023 / 2,083
analytics_platform ████████████████████ 98.0% 2,798 / 2,855
groups ████████████████████ 98.2% 1,823 / 1,856

Report-only. Patch coverage = changed backend lines covered vs origin/master. Sorted lowest first.
Known gaps: lines covered only by Temporal tests show as uncovered; core line numbers may drift if master changed the same file.

mypy flagged a possibly missing integration ID in the sender lookup. Without a sender there is no domain to check against, and the sender field already rejects that case.

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

Generated-By: PostHog Desktop
Task-Id: 90d9a51d-beba-47ba-8f50-b351327676f9
@mayteio
mayteio marked this pull request as ready for review October 22, 2026 08:20
@mayteio mayteio added the stamphog Request AI approval (no full review) label Oct 22, 2026 — with PostHog
@parameterai

parameterai Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

Risk: No findings

This PR adds a per-template email sender (integration ID, display name, custom from address) and reply-to to transactional email templates, copies them into the backing workflow on conversion and every publish, and validates the custom address against the sender's verified domain. I traced the authoring API, the conversion/publish service, and the nodejs send path end to end and found the new fields fail closed at each boundary: integration lookups are team-scoped, unverified senders are rejected at publish and send, and an off-domain or malformed custom address (including one produced by Liquid at send time) is discarded in favor of the sender's own address.

Sentinel reviewed f799de5 · Review settings

@pr-assigner-resolver-posthog
pr-assigner-resolver-posthog Bot requested a review from a team October 22, 2026 08:21

@stamphog stamphog Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Approved.

The disclosed API, write-path and sender-validation changes enter risky territory, with assurance supplied by owning-team authorship and STRONG familiarity. Regression tests cover sender changes, invalid integrations and legacy content; there are no outstanding review concerns or identified refusal grounds.

  • Author wrote 92% of the modified lines and has 38 merged PRs in these paths (familiarity STRONG).
Gate mechanics and policy version
Gate Result
prerequisites ✓ all clear
deny-list ✓ no deny categories matched
size ✓ 163L, 6F substantive, 623L/14F incl. docs/generated/snapshots — within ceiling
tier ✓ T1-agent / T1d-complex (623L, 14F, two-areas, feat)
stamphog 2.6.0 .stamphog/policy.yml @ f799de5 · reviewed head f799de5

@trunk-io

trunk-io Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

Static Badge   Static Badge   Static Badge

View Full Report ↗︎ ⋅ Docs

@trunk-io

trunk-io Bot commented Oct 22, 2026

Copy link
Copy Markdown

This pull request is queued for merge as part of 121079, which will merge 121871, 121079.

@trunk-io
trunk-io Bot merged commit 705811c into master Oct 22, 2026
406 checks passed
@trunk-io
trunk-io Bot deleted the posthog/transactional-email-sender-api branch October 22, 2026 09:43
@trunk-io

trunk-io Bot commented Oct 22, 2026

Copy link
Copy Markdown

This pull request was merged into master as part of stacked PR 121079.

@deployment-status-posthog

deployment-status-posthog Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

Deploy status

Environment Status Deployed At Workflow
dev ✅ Deployed 2026-10-22 10:06 UTC Run
prod-us ✅ Deployed 2026-10-22 10:20 UTC Run
prod-eu ✅ Deployed 2026-10-22 10:22 UTC Run

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

stamphog Request AI approval (no full review)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant