Skip to content

feat(messaging): read a transactional email's status by message id - #123932

Open
mayteio wants to merge 5 commits into
posthog/messaging-send-email-endpointfrom
posthog/messaging-email-status-lookup
Open

mayteio wants to merge 5 commits into
posthog/messaging-send-email-endpointfrom
posthog/messaging-email-status-lookup

Conversation

@mayteio

@mayteio mayteio commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

Problem

  • A caller of the transactional email API gets 202 with a message_id, and no way to find out whether the email went out.
  • Without that, a 202 followed by silence reads like a lost email, and a retry loop cannot check before it gives up.

Stacked on #123556. Refs #120921, whose read section this implements with a subset of its statuses.

Changes

  • GET /api/projects/:project_id/messaging/emails/:message_id/ returns { id, status, created_at, status_at, reason }.
  • A real send has a status within a few seconds of the 202. An unknown ID, or another project's, answers 404.
Status Where it comes from
queued The run the send API records in hog_invocation_results before it answers, while it waits or for a minute after it finishes while its send record arrives
sent The worker's record in message_assets of an email the provider accepted
failed A run that errored or was canceled (reason is the error kind), or one that finished without sending (reason is not_sent)
  • Both records are written by PostHog, never by a caller, and neither needs engagement capture.
  • The checks before the send API answers already refuse every email the worker would skip, so not_sent only covers a state change in the moments before the worker runs, such as a bounce that suppresses the recipient.
  • Delivery outcomes (delivered, bounced, complained) are left out. They need engagement capture and feat(workflows): tie email events to the send and version that caused them #123902, and they come from events anyone with the project token can capture. They can be added once provider outcomes have trusted storage.
  • The reads go through facades: fetch_invocation_lifecycle in the cdp facade and fetch_sent_email_asset in the workflows facade. Both search only the project's transactional workflows (each template's current one plus every API-triggered workflow), so each ClickHouse read stays on its table's sort key.
  • Both reads use Workload.OFFLINE: the callers are servers polling with a project secret API key, and no person waits on screen.
  • A session or personal API key reader needs viewer access to the email's workflow, as the send route needs editor access.
  • Project secret keys can now hold hog_flow:read, so a polling service doesn't need a key that can also send. No other hog_flow viewset accepts project keys.
  • Project key reads have their own rate-limit buckets (600 a minute and 20,000 an hour, per key and per project), so polling cannot use up the send budget. Other callers get the default API throttles.
  • The route is behind the same workflows-transactional-email-api flag as the send routes.
  • Mechanical: the regenerated messaging frontend and MCP types, a seeding helper in the workflows facade testing.py, and the dependency comments in tach.toml.

How did you test this code?

Test rationale:

  • test_transactional_email_status.py (new; the endpoint has no existing test) runs against ClickHouse with real run rows and send records. It covers each status, a send record winning over a run still marked running, a send with no run record, the grace window, a send from a workflow no template links to, a member without workflow access, 404 for unknown, foreign, malformed and future IDs, the read scope and the flag gate.
  • Mutation checks: the future-ID guard, the grace window, the not_sent rule, the access check and sent-before-running each make their test fail when removed.
  • test_message_assets.py gains a parameterized test for fetch_sent_email_asset. test_project_secret_api_keys.py creates a key with hog_flow:read, which fails without the allowlist entry.

Ran locally: the new file (16 pass), test_message_assets.py (46), test_transactional_email.py (59), test_project_secret_api_keys.py and test_scopes.py (205), repo-wide mypy --cache-fine-grained ., ruff, hogli lint:tach and hogli build:openapi.

Ran on a local stack, with the workflows dev intent, maildev and a project secret key:

Request Result
POST /messaging/send/email/ to a real address, then poll 202, 404 for 4 to 9 seconds, then sent; maildev got the email
A send the worker skipped (example.com, which #123878 refuses before the 202) failed, reason: not_sent
A run that errored failed, reason: hog_error
Unknown ID, malformed ID, no key 404, 404, 401
Read with a hog_flow:read key; send with it 200; 403

The stack run caught a 500 (an untagged ClickHouse query), fixed in this PR.

Release status

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

Docs update

None in this repository. The transactional email docs page in PostHog/posthog.com#21564 should document this endpoint.

🤖 Agent context

Autonomy: Human-driven (agent-assisted)

Agent: PostHog Desktop (Claude Code), Claude Opus 5.5

  • Skills: /improving-drf-endpoints, /writing-clickhouse-queries, /writing-dataclasses, /writing-tests, /writing-code-comments, /stacking-prs, /verifying-changes, /writing-pr-descriptions.
  • Earlier rounds read engagement events and the worker's skip logs too. Review showed events can be captured by anyone with the project token, and the pre-send checks already cover every skip, so the endpoint now answers from the two records PostHog writes itself.
  • Duplicate search: no open PR adds this endpoint.
  • Public artifact: all test data is invented (example.com).

🤖 Generated with Claude Code


Created with PostHog Desktop

@mayteio mayteio self-assigned this Oct 27, 2026
@mayteio
mayteio added this pull request to stack #123933 October 27, 2026 11:15
@mayteio mayteio added the posthog-review Run a Standard PostHog Review label Oct 27, 2026 — with PostHog
@github-actions

github-actions Bot commented Oct 27, 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 — 4% of added code lines are comments (26 of 610)

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_status.py 9 74
products/messaging/backend/presentation/views/transactional_email_status.py 5 112
products/cdp/backend/services/invocation_lifecycle.py 4 79
posthog/scopes.py 3 4
products/messaging/backend/facade/contracts.py 2 16
posthog/api/test/test_project_secret_api_keys.py 1 11
products/messaging/backend/tests/api/test_transactional_email_status.py 1 169
products/workflows/backend/services/message_assets.py 1 36

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

⚠️ Bundle size — 🔺 +16 B (+0.0%)

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

Total: 76.96 MiB · 🔺 +16 B (+0.0%)

No file changed by more than 1000 B.

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

⚠️ Eager graph — 1 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.00 MiB · 24 files no change █████████░ 87.8% 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.97 MiB · 620 files no change ██████████ 97.9% of 3.03 MiB
authenticated shell (every logged-in page)
src/scenes/AuthenticatedShell.tsx
7.49 MiB · 2,562 files no change ██████████ 100.0% of 7.50 MiB
dashboard scene
src/scenes/dashboard/Dashboard.tsx
9.94 MiB · 3,728 files no change ██████████ 99.3% 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.67 MiB · 2,655 files no change ██████████ 99.1% of 7.74 MiB
🟡 AI-first home path
src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/ai-first/AiFirstHomepage.tsx
7.92 MiB · 2,648 files no change ██████████ 100.1% of 7.91 MiB
events scene
src/scenes/activity/explore/EventsScene.tsx
9.38 MiB · 3,510 files no change ██████████ 97.9% of 9.59 MiB
web analytics scene
src/scenes/web-analytics/WebAnalyticsScene.tsx
10.31 MiB · 3,756 files no change ██████████ 95.2% of 10.83 MiB
replay detail scene
src/scenes/session-recordings/detail/SessionRecordingDetail.tsx
8.41 MiB · 3,087 files no change █████████░ 88.8% 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
🟢 src/layout/today/TodayShell.tsx stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/layout/today/TodayContextPanelFloating.tsx stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/layout/today/TodaySidePanel.tsx stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/layout/today/TodayTopBar.tsx stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/layout/today/todayRailItems.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
🟢 packages/quill/packages/charts/ stays out of src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/today/TodayHome.tsx
🟢 products/posthog_ai/frontend/components/tool/widgets/VisualizationWidget.tsx stays out of src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/ai-first/AiFirstHomepage.tsx
🟢 products/posthog_ai/frontend/components/tool/widgets/RecordingsWidget.tsx stays out of src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/ai-first/AiFirstHomepage.tsx
🟢 src/queries/Query/Query.tsx stays out of src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/ai-first/AiFirstHomepage.tsx
🟢 products/posthog_ai/frontend/api/primitives.ts stays out of src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/ai-first/AiFirstHomepage.tsx
🟢 src/scenes/notebooks/Notebook/notebookLogic.ts stays out of src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/ai-first/AiFirstHomepage.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.7 KiB ../node_modules/.pnpm/@jspm+core@2.1.0/node_modules/@jspm/core/nodelibs/browser/process.js
4.0 KiB ../node_modules/.pnpm/scheduler@0.23.2/node_modules/scheduler/cjs/scheduler.production.min.js
1.7 KiB ../node_modules/.pnpm/base64-js@1.5.1/node_modules/base64-js/index.js
1.6 KiB src/lib/utils/bootTiming.ts
1.6 KiB src/index.tsx
1.2 KiB src/RootErrorBoundary.tsx
949 B src/scenes/ChunkLoadErrorBoundary.tsx
Largest files eagerly shipped from src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
Size File
322.9 KiB ../node_modules/.pnpm/posthog-js@1.438.23_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
217.5 KiB ../node_modules/.pnpm/@posthog+icons@0.39.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
100.8 KiB src/products.tsx
86.1 KiB src/lib/api.ts
68.2 KiB src/lib/lemon-ui/icons/icons.tsx
41.9 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
36.4 KiB ../node_modules/.pnpm/kea@4.0.0-pre.6_patch_hash=139b8d1f1304f9d9da452a9a1244c94ea679dbcb85687d8999563146879fb6f5_react@18.3.1/node_modules/kea/lib/index.cjs.js
27.0 KiB ../node_modules/.pnpm/tailwind-merge@2.2.2/node_modules/tailwind-merge/dist/bundle-mjs.mjs
24.6 KiB ../node_modules/.pnpm/buffer@6.0.3/node_modules/buffer/index.js
Largest files eagerly shipped from src/scenes/AuthenticatedShell.tsx
Size File
322.9 KiB ../node_modules/.pnpm/posthog-js@1.438.23_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
286.4 KiB src/taxonomy/core-filter-definitions-by-group.json
239.9 KiB ../packages/quill/packages/quill/dist/index.js
217.5 KiB ../node_modules/.pnpm/@posthog+icons@0.39.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js
153.8 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
100.8 KiB src/products.tsx
94.5 KiB ../node_modules/.pnpm/@tiptap+core@3.20.6_@tiptap+pm@3.20.6/node_modules/@tiptap/core/dist/index.js
92.5 KiB ../node_modules/.pnpm/prosemirror-view@1.40.1/node_modules/prosemirror-view/dist/index.js
86.1 KiB src/lib/api.ts
Largest files eagerly shipped from src/scenes/dashboard/Dashboard.tsx
Size File
322.9 KiB ../node_modules/.pnpm/posthog-js@1.438.23_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
286.4 KiB src/taxonomy/core-filter-definitions-by-group.json
239.9 KiB ../packages/quill/packages/quill/dist/index.js
217.5 KiB ../node_modules/.pnpm/@posthog+icons@0.39.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js
153.8 KiB ../node_modules/.pnpm/re2js@0.4.1/node_modules/re2js/build/index.esm.js
144.1 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
100.8 KiB src/products.tsx
94.5 KiB ../node_modules/.pnpm/@tiptap+core@3.20.6_@tiptap+pm@3.20.6/node_modules/@tiptap/core/dist/index.js
92.5 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
322.9 KiB ../node_modules/.pnpm/posthog-js@1.438.23_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
286.4 KiB src/taxonomy/core-filter-definitions-by-group.json
239.9 KiB ../packages/quill/packages/quill/dist/index.js
217.5 KiB ../node_modules/.pnpm/@posthog+icons@0.39.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js
153.8 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
100.8 KiB src/products.tsx
94.5 KiB ../node_modules/.pnpm/@tiptap+core@3.20.6_@tiptap+pm@3.20.6/node_modules/@tiptap/core/dist/index.js
92.5 KiB ../node_modules/.pnpm/prosemirror-view@1.40.1/node_modules/prosemirror-view/dist/index.js
86.1 KiB src/lib/api.ts
Largest files eagerly shipped from src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/ai-first/AiFirstHomepage.tsx
Size File
322.9 KiB ../node_modules/.pnpm/posthog-js@1.438.23_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
286.4 KiB src/taxonomy/core-filter-definitions-by-group.json
239.9 KiB ../packages/quill/packages/quill/dist/index.js
217.5 KiB ../node_modules/.pnpm/@posthog+icons@0.39.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js
153.8 KiB ../node_modules/.pnpm/re2js@0.4.1/node_modules/re2js/build/index.esm.js
144.1 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
100.8 KiB src/products.tsx
94.5 KiB ../node_modules/.pnpm/@tiptap+core@3.20.6_@tiptap+pm@3.20.6/node_modules/@tiptap/core/dist/index.js
92.5 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
322.9 KiB ../node_modules/.pnpm/posthog-js@1.438.23_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
286.4 KiB src/taxonomy/core-filter-definitions-by-group.json
239.9 KiB ../packages/quill/packages/quill/dist/index.js
217.5 KiB ../node_modules/.pnpm/@posthog+icons@0.39.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js
153.8 KiB ../node_modules/.pnpm/re2js@0.4.1/node_modules/re2js/build/index.esm.js
144.1 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
100.8 KiB src/products.tsx
94.5 KiB ../node_modules/.pnpm/@tiptap+core@3.20.6_@tiptap+pm@3.20.6/node_modules/@tiptap/core/dist/index.js
92.5 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
322.9 KiB ../node_modules/.pnpm/posthog-js@1.438.23_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
286.4 KiB src/taxonomy/core-filter-definitions-by-group.json
239.9 KiB ../packages/quill/packages/quill/dist/index.js
217.5 KiB ../node_modules/.pnpm/@posthog+icons@0.39.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js
182.9 KiB src/queries/validators.js
153.8 KiB ../node_modules/.pnpm/re2js@0.4.1/node_modules/re2js/build/index.esm.js
144.1 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
100.8 KiB src/products.tsx
94.5 KiB ../node_modules/.pnpm/@tiptap+core@3.20.6_@tiptap+pm@3.20.6/node_modules/@tiptap/core/dist/index.js
Largest files eagerly shipped from src/scenes/session-recordings/detail/SessionRecordingDetail.tsx
Size File
322.9 KiB ../node_modules/.pnpm/posthog-js@1.438.23_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
316.1 KiB ../node_modules/.pnpm/posthog-js@1.438.23_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/rrweb.js
286.4 KiB src/taxonomy/core-filter-definitions-by-group.json
239.9 KiB ../packages/quill/packages/quill/dist/index.js
217.5 KiB ../node_modules/.pnpm/@posthog+icons@0.39.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js
153.8 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
100.8 KiB src/products.tsx
94.5 KiB ../node_modules/.pnpm/@tiptap+core@3.20.6_@tiptap+pm@3.20.6/node_modules/@tiptap/core/dist/index.js
92.5 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.30 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.30 MiB · 18 files no change ████░░░░░░ 40.2% of 5.72 MiB
Deferred (lazy) 2.11 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
911.5 KiB dist/toolbar/toolbar-app-DUCPI2WS.css
694.6 KiB dist/toolbar/chunk-chunk-GZTRHRCK.js
259.9 KiB dist/toolbar/chunk-chunk-ONRHJSKR.js
131.8 KiB dist/toolbar/chunk-chunk-FDH2IBXT.js
130.4 KiB dist/toolbar/chunk-chunk-AME52YTL.js
74.8 KiB dist/toolbar/toolbar-app-VEELPFRX.js
69.0 KiB dist/toolbar/chunk-chunk-TSAL54PB.js
35.6 KiB dist/toolbar/chunk-chunk-KVA34CN3.js
21.7 KiB dist/toolbar/chunk-chunk-GOU3MNDU.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 — 🔺 +2.3 KiB (+0.0%)

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

Total: 970.25 MiB · 🔺 +2.3 KiB (+0.0%)

ℹ️ MCP UI apps size — 35 app(s), 28999.8 KB JS

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

App JS CSS
debug 844.3 KB 211.1 KB
explain-chart 1004.9 KB 211.1 KB
action 767.7 KB 211.1 KB
action-list 835.3 KB 211.1 KB
cohort 766.7 KB 211.1 KB
cohort-list 834.3 KB 211.1 KB
email-template 766.6 KB 211.1 KB
error-details 771.6 KB 211.1 KB
error-issue 768.9 KB 211.1 KB
error-issue-list 836.8 KB 211.1 KB
experiment 832.4 KB 211.1 KB
experiment-list 836.1 KB 211.1 KB
experiment-results 835.0 KB 211.1 KB
feature-flag 834.7 KB 211.1 KB
feature-flag-list 838.4 KB 211.1 KB
feature-flag-testing 770.0 KB 211.1 KB
inline-scan 767.2 KB 211.1 KB
insight-actors 833.8 KB 211.1 KB
llm-costs 831.1 KB 211.1 KB
session-recording 769.0 KB 211.1 KB
survey 768.3 KB 211.1 KB
survey-global-stats 831.1 KB 211.1 KB
survey-list 836.0 KB 211.1 KB
survey-stats 831.1 KB 211.1 KB
trace-span 767.1 KB 211.1 KB
trace-span-list 835.2 KB 211.1 KB
vision-observation-list 834.4 KB 211.1 KB
workflow 767.1 KB 211.1 KB
workflow-list 834.7 KB 211.1 KB
workflow-setup-status 766.7 KB 211.1 KB
query-results 1087.6 KB 211.1 KB
render-ui 1186.5 KB 211.1 KB
visual-review-snapshots 771.6 KB 211.1 KB
workflow-data-suggestions 769.4 KB 211.1 KB
workflow-sending-rules 768.1 KB 211.1 KB
✅ Playwright — all passed

All tests passed.

View test results →

⚠️ Backend coverage — 92.0% of changed backend lines covered — 34 uncovered

🧪 Backend test coverage

Patch coverage — changed backend lines (products + core): ██████████████████░░ 92.0% (412 / 446)

File Patch Uncovered changed lines
products/cdp/backend/services/invocation_lifecycle.py 37.0% 16–20, 22–24, 35–37, 46, 73–77
products/workflows/backend/facade/api.py 50.0% 234
products/messaging/backend/facade/contracts.py 78.9% 121–124
products/workflows/backend/facade/testing.py 80.0% 24
products/messaging/backend/presentation/views/message_templates.py 90.0% 534–538, 613–614
products/messaging/backend/presentation/views/transactional_email_status.py 95.7% 123, 133, 135
products/messaging/backend/services/transactional_email_status.py 98.7% 73

🤖 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 334539625669778 -n patch-coverage), or the coverage-data block at the end of this comment.

Per-product line coverage (touched products)
Product Coverage Lines
platform_features ██░░░░░░░░░░░░░░░░░░ 12.1% 7 / 58
demo ███████████░░░░░░░░░ 56.4% 1,542 / 2,736
data_tools ████████████░░░░░░░░ 60.9% 95 / 156
warehouse_sources_queue █████████████░░░░░░░ 65.3% 2,159 / 3,306
authorization ████████████████░░░░ 79.7% 181 / 227
batch_exports ████████████████░░░░ 81.6% 22,901 / 28,059
engineering_analytics █████████████████░░░ 85.0% 11,463 / 13,482
commands █████████████████░░░ 85.5% 1,618 / 1,893
mcp_analytics █████████████████░░░ 86.2% 5,720 / 6,638
ml_inference █████████████████░░░ 86.3% 524 / 607
apm █████████████████░░░ 86.6% 1,616 / 1,866
posthog_ai ██████████████████░░ 87.9% 4,296 / 4,886
notebooks ██████████████████░░ 88.5% 15,330 / 17,321
dashboards ██████████████████░░ 88.6% 7,785 / 8,785
warehouse_suggestions ██████████████████░░ 88.6% 3,047 / 3,438
mcp_registry ██████████████████░░ 89.0% 1,596 / 1,794
webmcp ██████████████████░░ 89.3% 292 / 327
cohorts ██████████████████░░ 89.6% 8,568 / 9,559
product_tours ██████████████████░░ 89.7% 1,355 / 1,510
signals ██████████████████░░ 90.0% 88,830 / 98,662
visual_review ██████████████████░░ 90.6% 9,858 / 10,875
tasks ██████████████████░░ 90.7% 79,899 / 88,111
managed_warehouse ██████████████████░░ 91.0% 12,251 / 13,458
data_warehouse ██████████████████░░ 91.1% 15,325 / 16,821
canvas ██████████████████░░ 91.2% 8,189 / 8,980
aeo ██████████████████░░ 91.2% 1,602 / 1,756
cdp ██████████████████░░ 91.4% 10,293 / 11,266
data_modeling ██████████████████░░ 91.4% 10,729 / 11,735
autoresearch ██████████████████░░ 91.4% 11,618 / 12,705
today ██████████████████░░ 91.6% 3,976 / 4,341
business_knowledge ██████████████████░░ 91.6% 10,017 / 10,934
metrics ██████████████████░░ 91.7% 10,627 / 11,590
slack_app ██████████████████░░ 91.7% 21,465 / 23,406
wizard ██████████████████░░ 91.7% 6,870 / 7,490
streamlit_apps ██████████████████░░ 91.9% 3,139 / 3,415
error_tracking ██████████████████░░ 92.0% 18,957 / 20,595
conversations ██████████████████░░ 92.2% 30,448 / 33,008
review_hog ██████████████████░░ 92.3% 17,915 / 19,413
early_access_features ███████████████████░ 92.6% 1,339 / 1,446
managed_migrations ███████████████████░ 92.6% 1,630 / 1,760
alerts_platform ███████████████████░ 92.7% 8,035 / 8,667
stamphog ███████████████████░ 92.7% 8,882 / 9,579
notifications ███████████████████░ 92.8% 1,216 / 1,310
alerts ███████████████████░ 92.9% 10,025 / 10,793
web_analytics ███████████████████░ 93.3% 26,068 / 27,943
ai_training ███████████████████░ 93.3% 598 / 641
cross_project_dashboards ███████████████████░ 93.4% 880 / 942
surveys ███████████████████░ 93.4% 6,941 / 7,429
exports ███████████████████░ 93.6% 4,166 / 4,452
ai_gateway ███████████████████░ 93.6% 787 / 841
approvals ███████████████████░ 93.7% 4,994 / 5,331
context_layer ███████████████████░ 93.8% 3,981 / 4,244
mcp_store ███████████████████░ 93.9% 9,188 / 9,784
marketing_analytics ███████████████████░ 93.9% 21,234 / 22,605
ai_observability ███████████████████░ 94.0% 29,256 / 31,128
billing_alerts ███████████████████░ 94.0% 2,322 / 2,470
customer_analytics ███████████████████░ 94.1% 27,892 / 29,651
messaging ███████████████████░ 94.2% 5,122 / 5,439
workflows ███████████████████░ 94.3% 24,555 / 26,042
legal_documents ███████████████████░ 94.4% 2,313 / 2,451
logs ███████████████████░ 94.4% 19,281 / 20,429
replay_vision ███████████████████░ 94.4% 32,767 / 34,708
actions ███████████████████░ 94.7% 1,004 / 1,060
reminders ███████████████████░ 94.8% 760 / 802
experiments ███████████████████░ 94.8% 39,506 / 41,656
endpoints ███████████████████░ 94.9% 9,351 / 9,858
growth ███████████████████░ 94.9% 11,347 / 11,959
annotations ███████████████████░ 95.1% 1,095 / 1,151
skills ███████████████████░ 95.2% 7,630 / 8,015
uptime_monitoring ███████████████████░ 95.3% 306 / 321
access_control ███████████████████░ 95.5% 8,105 / 8,491
subscriptions ███████████████████░ 95.6% 13,601 / 14,228
data_catalog ███████████████████░ 95.7% 4,714 / 4,927
product_analytics ███████████████████░ 95.8% 29,875 / 31,195
feature_flags ███████████████████░ 95.8% 28,559 / 29,813
revenue_analytics ███████████████████░ 95.9% 1,877 / 1,958
data_quality ███████████████████░ 96.0% 8,561 / 8,915
tracing ███████████████████░ 96.4% 4,360 / 4,524
warehouse_sources ███████████████████░ 96.5% 479,077 / 496,432
security ███████████████████░ 96.7% 1,549 / 1,602
account_audits ███████████████████░ 96.7% 770 / 796
pulse ███████████████████░ 97.1% 2,023 / 2,083
analytics_platform ████████████████████ 98.0% 2,828 / 2,885
groups ████████████████████ 98.2% 1,838 / 1,871
field_notes ████████████████████ 99.4% 172 / 173

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.

@posthog posthog Bot 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.

PostHog Review · Standard · reviewed 4a93817: 1 Must fix, 6 Should fix.

This Standard review started from the posthog-review label. Comment @posthog review --deep for a Deep review, or @posthog help to see all commands.

See this review and its findings in PostHog Review.

Comment thread products/messaging/backend/services/transactional_email_status.py Outdated
Comment thread products/messaging/backend/services/transactional_email_status.py Outdated
Comment thread products/messaging/backend/services/transactional_email_status.py Outdated
Comment thread products/messaging/backend/services/transactional_email_status.py Outdated
Comment thread products/messaging/backend/services/transactional_email_status.py
Comment thread products/messaging/backend/services/transactional_email_status.py Outdated
@posthog posthog Bot removed the posthog-review Run a Standard PostHog Review label Oct 27, 2026
@trunk-io

trunk-io Bot commented Oct 27, 2026 •

Copy link
Copy Markdown

Static Badge   Static Badge   Static Badge

Failed Test Failure Summary Logs
Scenes-App/Experiments ExperimentWithHealthFindings play-test The Storybook test could not find the text “Why?” because the experiment story remained stuck on a loading spinner. Logs ↗︎
Scenes-App/People/Person PersonNotFound smoke-test The page timed out waiting for loading indicators to disappear. Logs ↗︎

View Full Report ↗︎ ⋅ Docs

@mayteio mayteio added the posthog-review Run a Standard PostHog Review label Oct 27, 2026 — with PostHog

@posthog posthog Bot 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.

PostHog Review · Standard · reviewed f5327a1: 1 Must fix, 3 Should fix.

This Standard review started from the posthog-review label. Comment @posthog review --deep for a Deep review, or @posthog help to see all commands.

Comment thread products/messaging/backend/services/transactional_email_status.py Outdated
Comment thread products/messaging/backend/services/transactional_email_status.py Outdated
Comment thread products/messaging/backend/services/transactional_email_status.py Outdated
Comment thread products/messaging/backend/services/transactional_email_status.py Outdated
@posthog posthog Bot removed the posthog-review Run a Standard PostHog Review label Oct 27, 2026
@mayteio mayteio added the posthog-review Run a Standard PostHog Review label Oct 27, 2026 — with PostHog

@posthog posthog Bot 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.

PostHog Review · Standard · reviewed 681be4a: 2 Should fix.

This is review round 3 on this pull request. When findings keep coming back in the same code, the approach is usually the problem. Before the next fix, check whether a different design would remove the whole class of findings.

This Standard review started from the posthog-review label. Comment @posthog review --deep for a Deep review, or @posthog help to see all commands.

@posthog posthog Bot removed the posthog-review Run a Standard PostHog Review label Oct 27, 2026
@mayteio mayteio added the posthog-review Run a Standard PostHog Review label Oct 27, 2026 — with PostHog

@posthog posthog Bot 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.

PostHog Review · Standard · reviewed ca5df7d: no findings.

This Standard review started from the posthog-review label. Comment @posthog review --deep for a Deep review, or @posthog help to see all commands.

@posthog posthog Bot removed the posthog-review Run a Standard PostHog Review label Oct 27, 2026
@mayteio
mayteio marked this pull request as ready for review October 27, 2026 12:26
@mayteio mayteio added the stamphog Request AI approval (no full review) label Oct 27, 2026 — with PostHog
@parameterai

parameterai Bot commented Oct 27, 2026 •

Copy link
Copy Markdown

Risk: No findings

This increment adds a recipient_unreachable refusal path to the transactional email send API (new exception, service mapping, and a 422 status mapping), plus facade re-exports and regenerated types. The refusal code is only used as a dict-key lookup against static messages, so it introduces no new attack surface; the endpoint's auth, throttles, and access checks are unchanged from the previously reviewed version.

Sentinel reviewed 905ea4a · Review settings

@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.

Not approved — this change needs a human reviewer.

Re-adding the stamphog label gives the same result unless the changed files change.

Two gates refused this pull request. The deny-list gate flagged it for matching crypto_secrets. The change touches posthog/scopes.py and frontend/src/lib/scopes.tsx to let project secret API keys hold hog_flow:read. The tier gate classified it as T2-never: a 1021-line, 22-file cross-cutting feature. The size gate passed, so size alone is not the problem.

A human reviewer needs to look at this one. The author can request a review from a code owner of the secret-key scope and permission code. Splitting the scope change from the new status endpoint and its facades would also give reviewers smaller pieces.

  • posthog[bot] reviewed the current head.
Gate mechanics and policy version
Gate Result
prerequisites ✓ all clear
deny-list ✗ matches: crypto_secrets
size ✓ 569L, 15F substantive, 1021L/22F incl. docs/generated/snapshots — within ceiling
tier ✗ classified as T2-never: T2-never (1021L, 22F, cross-cutting, feat)
stamphog 2.6.1 .stamphog/policy.yml @ unknown · reviewed head ca5df7d

@stamphog stamphog Bot removed the stamphog Request AI approval (no full review) label Oct 27, 2026
@pr-assigner-resolver-posthog
pr-assigner-resolver-posthog Bot requested a review from a team October 27, 2026 12:27
@pr-assigner-resolver-posthog

Copy link
Copy Markdown

👀 Auto-assigned reviewers

These soft owners were skipped because they only have minor changes here. Nothing blocks merge, so self-assign if you'd like a look:

  • @PostHog/team-devex (owners.yaml)

Soft owners come from each directory's owners.yaml and each product's product.yaml (resolved nearest-file-wins). For a skipped owner, the locator is the file that decided it. Generated files and lockfiles are ignored when deciding ownership.

Comment thread products/messaging/backend/services/transactional_email_status.py Outdated
@veria-ai

veria-ai Bot commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

PR overview

All previously flagged issues have been addressed. No open security concerns remain on this pull request.

Security review

No open security issues remain on this pull request.

Fixed/addressed: 1 · PR risk: 0/10

@mayteio mayteio added the posthog-review Run a Standard PostHog Review label Oct 27, 2026 — with PostHog

@posthog posthog Bot 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.

PostHog Review · Standard · reviewed 9325944: no findings.

This Standard review started from the posthog-review label. Comment @posthog review --deep for a Deep review, or @posthog help to see all commands.

@posthog posthog Bot removed the posthog-review Run a Standard PostHog Review label Oct 27, 2026
@mayteio mayteio added the stamphog Request AI approval (no full review) label Oct 27, 2026 — with PostHog

@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.

Not approved — this change needs a human reviewer.

Re-adding the stamphog label gives the same result unless the changed files change.

stamphog won't auto-review this pull request because two gates refused it. The deny-list gate matched the crypto_secrets category, most likely because the change widens which scopes a project secret API key can hold (posthog/scopes.py, frontend/src/lib/scopes.tsx, and the related test in test_project_secret_api_keys.py). The tier gate classified it as T2-never: it is a cross-cutting feature spanning 21 files and 809 lines across the cdp, messaging, and workflows products, plus the API and scope layers.

Please ask a human reviewer to look at it. If you want to shrink the review, you could split the project secret key scope change into its own pull request, separate from the new status endpoint and its facades.

  • posthog[bot] reviewed the current head.
Gate mechanics and policy version
Gate Result
prerequisites ✓ all clear
deny-list ✗ matches: crypto_secrets
size ✓ 463L, 15F substantive, 809L/21F incl. docs/generated/snapshots — within ceiling
tier ✗ classified as T2-never: T2-never (809L, 21F, cross-cutting, feat)
stamphog 2.6.1 .stamphog/policy.yml @ unknown · reviewed head 9325944

@stamphog stamphog Bot removed the stamphog Request AI approval (no full review) label Oct 27, 2026
GET /api/projects/:project_id/messaging/emails/:message_id/ returns { id, status, created_at, status_at, reason } for a transactional send.

- queued: the send API records the run before it answers 202, so a real send has a status from the start.
- sent: the email worker's record of every email the provider accepts (message_assets).
- skipped: the run finished without a send record, after a short grace for the record to arrive.
- failed: the run failed, or a $workflows_email_failed event exists.
- delivered, bounced, complained: from $workflows_email_* events keyed by $workflow_invocation_id, which need engagement capture and the change in #123902.

Reads go through the cdp facade (fetch_invocation_lifecycle) and the workflows facade (fetch_sent_email_asset), scoped to the project's transactional workflows so each ClickHouse read stays on its sort key. Reads have their own rate-limit buckets and need hog_flow:read.

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

Generated-By: PostHog Desktop
Task-Id: e72a2c10-f145-4f7c-8754-a4fcc06a18be
…r's log

- A session or personal API key reader needs viewer access to the email's workflow, as the send route needs editor access.
- Tag the ClickHouse queries once before any read. The run lookup ran untagged and the endpoint answered 500 on a local stack.
- A confirmed send wins over a run still marked running, and the send record and events still answer when an instance has the run records turned off.
- skipped now comes from the worker's "Skipping send:" log, with its reason. A finished run with neither a send record nor a skip log is unknown.
- Engagement events are aggregated per event before the result is read, so a long run of opens can't hide a later bounce or complaint, and the query filters on the send's distinct_id, which is in the events sort key.
- Workflows an API call triggers count as transactional too, so a send from a workflow a template was relinked away from still resolves.
- The 404 window right after a send is a few seconds, not one or two.

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

Generated-By: PostHog Desktop
Task-Id: e72a2c10-f145-4f7c-8754-a4fcc06a18be
Anyone with a project's public token can capture a $workflows_email_* event, so events no longer decide failed or sent. failed comes from the run record alone, which keeps its error kind and follows a successful rerun. Events are read only when the worker's send record exists, and then only to refine it to delivered, bounced or complained, filtered on the send record's distinct_id. An unknown ID no longer scans the project's email events.

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

Generated-By: PostHog Desktop
Task-Id: e72a2c10-f145-4f7c-8754-a4fcc06a18be
…ssion reads

- Project secret keys can now hold hog_flow:read, so a service that polls a transactional email's status doesn't need a key that can also send. No other hog_flow viewset accepts project keys, so the scope opens nothing else.
- Session and personal API key callers of the status route keep the default API throttles. The email read buckets only count project keys.

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

Generated-By: PostHog Desktop
Task-Id: e72a2c10-f145-4f7c-8754-a4fcc06a18be
…failed

The status now comes from two records PostHog writes itself: the run the send API records before it answers, and the worker's record of an email the provider accepted. queued while the run waits or its send record is on the way, sent once the send record exists, failed when the run errored (reason is the error kind) or finished without sending (reason not_sent).

The checks before the send API answers already refuse every email the worker would skip, so a separate skipped status and its log read are dropped. Delivered, bounced and complained are dropped too: they need engagement capture, depend on #123902, and come from events anyone with the project token can capture. They can come back once provider outcomes have trusted storage.

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

Generated-By: PostHog Desktop
Task-Id: e72a2c10-f145-4f7c-8754-a4fcc06a18be
@mayteio
mayteio force-pushed the posthog/messaging-email-status-lookup branch from 9325944 to 905ea4a Compare October 27, 2026 17:25

This branch has not been deployed

No deployments
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.

1 participant