Skip to content

docs(workflows): send Nodemailer mail through PostHog - #21987

Draft
Silthus wants to merge 2 commits into
docs/transactional-email-api-code-formfrom
docs/transactional-email-nodemailer
Draft

Silthus wants to merge 2 commits into
docs/transactional-email-api-code-formfrom
docs/transactional-email-nodemailer

Conversation

@Silthus

@Silthus Silthus commented Nov 5, 2026

Copy link
Copy Markdown
Contributor

Changes

A developer whose app sends email with Nodemailer finds no page that tells them how to send that email through PostHog.

This PR adds Send transactional emails with Nodemailer under Send transactional emails. The page shows how to install posthog-node and nodemailer, create the transport with createPostHogTransport from posthog-node/nodemailer, and keep each sendMail call. It also covers the key and host, the posthog options for one message, a stable idempotency_key for mail from a job queue, and the messages that PostHog refuses (EPOSTHOGUNSUPPORTED). The Node.js page's code-send section links to the page, and the sidebar gets a Nodemailer entry after Node.js.

Ticket: https://github.com/PostHog/michaelr-context/issues/41 (private).

Stacked on #21819. Merge it first. The transport ships in posthog-node with the PostHog/posthog-js draft PRs of the same ticket. This page stays a draft until that release is out.

Seams for review. The sidebar in src/navs/index.js is a hot file. #21932 adds SDK entries between Python and API, and #21984 adds React Email after API. This PR adds its entry after Node.js, so its hunk touches neither. Check for cross-stack breaks: the same concept documented twice (retries and idempotency keys on the Node.js page and on this page), and anything that conflicts with #21932 or #21984.

Merge danger

Low, two-way door. Docs only: one new page, one link, and one sidebar entry. A revert removes them.

Evidence

  • prettier --check src/navs/index.js passes.
  • Page check and before/after sidebar screenshots: these follow from the preview build.

Decisions

Question Answer Who Where
Where does the sidebar entry go? After Node.js, so its hunk does not touch #21932 or #21984 👍 Accepted default ticket
Does the page document every refusal? Yes, one table row for each refusal code, with the fix where one exists 🤖 Agent ticket

Checklist

  • I've read the docs and/or content style guides.
  • Words are spelled using American English
  • Use relative URLs for internal links
  • I've checked the pages added or changed in the Vercel preview build
  • If I moved a page, I added a redirect in vercel.json (no page moved)

🤖 Generated with Claude Code

Add a Nodemailer page under Send transactional emails: install, the
PostHog transport, key and host, the posthog options, retries from a
queue with a stable idempotency_key, and the messages PostHog refuses.
Link it from the Node.js page and add a sidebar entry after Node.js.

Ticket: https://github.com/PostHog/michaelr-context/issues/41

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

github-actions Bot commented Nov 5, 2026 •

Copy link
Copy Markdown
Contributor

Deploy preview

Status Details Updated (UTC)
🟢 Ready View preview Nov 05, 2026 01:15PM

Changed pages

Page Source
Send transactional emails with Node.js contents/docs/workflows/transactional-emails/node.mdx
Send transactional emails with Nodemailer contents/docs/workflows/transactional-emails/nodemailer.mdx

@github-actions github-actions Bot added the docs Improvements or additions to product documentation, "Docs" label Nov 5, 2026
@github-actions

github-actions Bot commented Nov 5, 2026 •

Copy link
Copy Markdown
Contributor

Vale prose linter → found 0 errors, 5 warnings, 0 suggestions in your markdown

Full report → Copy the linter results into an LLM to batch-fix issues.

Linter being weird? Update the rules!

contents/docs/workflows/transactional-emails/node.mdx — 0 errors, 1 warnings, 0 suggestions
Line Severity Message Rule
102:160 warning 'Nodemailer' is a possible misspelling. PostHogBase.Spelling
contents/docs/workflows/transactional-emails/nodemailer.mdx — 0 errors, 4 warnings, 0 suggestions
Line Severity Message Rule
2:39 warning 'Nodemailer' is a possible misspelling. PostHogBase.Spelling
7:31 warning 'Nodemailer' is a possible misspelling. PostHogBase.Spelling
19:96 warning 'Nodemailer' is a possible misspelling. PostHogBase.Spelling
82:70 warning 'Nodemailer's' is a possible misspelling. PostHogBase.Spelling

@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 7c0f31f: 1 Should fix.

See this review and its findings in PostHog Review. Comment @posthog review --deep for a Deep review. A Deep review takes longer and looks deeper than a Standard review.


- **`template_key`** names the email, such as `order-shipped`. PostHog groups the email's metrics and sends under it. Without it, the transport uses `defaultTemplateKey` from `createPostHogTransport`, or `nodemailer`. Use letters, digits, hyphens, and underscores.
- **`distinct_id`** records the email against the person with this distinct ID. Without it, PostHog uses a person whose distinct ID is the `to` address. See [link emails to your users](/docs/workflows/transactional-emails/node#link-emails-to-your-users).
- **`idempotency_key`** identifies this one email. PostHog sends an email only once for each key.

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.

P2 · Limit the idempotency guarantee to its 24-hour window

When a queue retries a send more than 24 hours after PostHog queued it, the same idempotency_key no longer prevents another email. The transactional-email overview documents this expiration, but this unconditional guarantee tells readers that a stable key always prevents duplicates. State the 24-hour window and link the queue guidance to that limit.

@github-actions

github-actions Bot commented Nov 5, 2026

Copy link
Copy Markdown
Contributor

Bundle report

Total JS (gzip)

10.27 MiB (-1480.1 KiB / -12.3%)

Largest changed named chunks
Chunk Gzip Δ vs master
app.js 2361.2 KiB +1270.1 KiB / +116.4%
23b110d95c587435d17e5a559280a7b225e5608c.js 110.1 KiB new
655c369601e38a7136cffdc999798d51fd71fc19.js 50.7 KiB new
4e5c9a1d4810c5f5c6325835bcfd7f436eb1af7e.js 9.3 KiB -33.4 KiB / -78.2%
d874fb577ecb130d9c247b410613f0930a236c94.js 20.0 KiB new
af98e9f54e85b4e4080569f09ec6b2f06c3d7b85.js 15.0 KiB new
component---src-pages-careers-tsx.js 54.5 KiB -10.9 KiB / -16.6%
component---src-pages-r-product-analytics-tsx.js 6.7 KiB -9.4 KiB / -58.3%
component---src-pages-r-posthog-mcp-tsx.js 4.3 KiB -9.3 KiB / -68.2%
component---src-pages-merch-tsx.js 22.0 KiB -8.8 KiB / -28.5%
component---src-pages-docs-about-tsx.js 0.8 KiB -7.4 KiB / -90.5%
component---src-pages-self-driving-index-tsx.js 9.2 KiB -7.3 KiB / -44.1%
component---src-pages-changelog-index-tsx.js 0.1 KiB -6.1 KiB / -97.8%
component---src-pages-teams-[slug]-tsx.js 8.9 KiB -6.1 KiB / -40.5%
component---src-pages-hog-index-tsx.js 0.5 KiB -5.6 KiB / -91.9%

Eager graph (modules shipped in each entrypoint's initial chunks)

Entrypoint Eager size Budget Modules
✅ app 20.92 MiB (+11246.9 KiB / +110.5%) report-only 2119
Largest modules in the app closure
Module Size
./src/data/mcp-tools.json 1491.9 KiB
css ./node_modules/.pnpm/css-loader@5.2.7_webpack@5.101.3/node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[8].oneOf[1].use[1]!./node_modules/.pnpm/postcss-loader@4.3.0_postcss@8.5.6_webpack@5.101.3/node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[8].oneOf[1].use[2]!./src/styles/global.css 838.0 KiB
./src/components/Stickers/Stickers.tsx 696.4 KiB
./node_modules/.pnpm/@posthog+brand@0.13.0_react@18.3.1/node_modules/@posthog/brand/dist/generated/hoggies/svg/x-ray.mjs 481.2 KiB
./node_modules/.pnpm/@posthog+brand@0.13.0_react@18.3.1/node_modules/@posthog/brand/dist/generated/hoggies/svg/trenchcoat.mjs 450.8 KiB
./node_modules/.pnpm/rehype-raw@7.0.0/node_modules/rehype-raw/lib/index.js + 29 modules 395.1 KiB
./node_modules/.pnpm/@posthog+brand@0.13.0_react@18.3.1/node_modules/@posthog/brand/dist/generated/hoggies/svg/im-the-driver.mjs 385.8 KiB
./src/hooks/useCustomers.tsx + 61 modules 381.8 KiB
./node_modules/.pnpm/@posthog+brand@0.13.0_react@18.3.1/node_modules/@posthog/brand/dist/generated/hoggies/svg/tourist.mjs 364.6 KiB
./node_modules/.pnpm/@posthog+icons@0.36.6_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js 354.8 KiB
./node_modules/.pnpm/react-markdown@8.0.7_@types+react@16.14.66_react@18.3.1/node_modules/react-markdown/lib/react-markdown.js + 88 modules 351.4 KiB
./src/components/ProductComparisonTable/index.tsx + 130 modules 314.3 KiB
./node_modules/.pnpm/framer-motion@14.0.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/framer-motion/dist/es/render/components/motion/proxy.mjs + 126 modules 287.2 KiB
./node_modules/.pnpm/@posthog+brand@0.13.0_react@18.3.1/node_modules/@posthog/brand/dist/generated/hoggies/svg/doll-house.mjs 284.1 KiB
./node_modules/.pnpm/cloudinary-core@2.14.0_lodash@4.17.21/node_modules/cloudinary-core/cloudinary-core.js 281.9 KiB

Eager-graph budgets are report-only until a baseline is established. Sizes are gzip of public/**/*.js; eager size is webpack module source bytes for the modules actually shipped in the entrypoint's initial chunks (post-tree-shake).

Name the 24-hour key window for queued mail, and list the attachment refusals.

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

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

docs Improvements or additions to product documentation, "Docs"

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant