Repository navigation
Conversation
Teaches a coding agent to send one transactional email from server code with PostHog: verify a sender through the PostHog MCP first, keep the template as an HTML file in the repository, upload it by key, and send it with posthog-node or the HTTP API. Ships the transactional email API docs page as its reference. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Check the PostHog MCP before any sender work, never recreate an existing sender, bound verification polling, set the default sender through a verify, ask before any test send, and cover Next.js and edge runtimes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 17, 2026
Closed
… file The MCP upload passes source.repository from the origin remote without credentials, source.path from the repository root and source.ref from HEAD. A new section sends a change to a code-managed template to its file and leaves the upload to posthog-node or the deploy. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A reverted file goes live only through an upload, because a send with an older published hash gets the newer content. Any send from the changed code publishes it. A by-key change is uploaded after the change. An agent without the repository tells the person or requests the change. The SDK always sends a path, so only repository and ref keep their stored value. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A revert to published content needs an upload only for templateFile(). A by-key change waits for the deploy step when there is one. A test through workflows-send-email publishes nothing, and an agent without the repository shares the PostHog Code task link. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 20, 2026
Closed
The skill now writes a local template in the app's code and sends it with templateKey, subject, html and templateData. The first send uploads it, so the MCP upload, templateFile() and the deploy-time upload by key go. Rendered content and templates made in PostHog (templateId) are the other two forms. Sends carry an idempotency key and retry with it. Job runners pass maxRetries: 0 and a key derived from the email's purpose. STEP 9 lists the contract's codes, including the 409 for a template edited in PostHog. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Inside the Wizard the agent asks through wizard_ask, writes the secret key with set_env_values, reaches PostHog tools through posthog_exec, and ends a blocked run with an [ABORT] reason the Wizard's transactional-email program matches. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Wizard's login may lack the workflows and integrations scopes, so a hidden tool there no longer stops the run as "API not enabled". The Wizard section names its tools as the runtime shows them, and replyTo no longer needs a verified sender. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…test send Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 22, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
A coding agent asked to "send a welcome email" has no PostHog skill to reach for. PostHog can send a transactional email from code (the transactional email API, in beta), but nothing tells an agent to verify a sender first, keep the email as a template in the app's code, and wire the send into the server.
With this skill, the agent verifies a sender, writes the email as a local template in code, and sends it with
posthog.emails.send({ templateKey, subject, html, templateData }). The first send uploads the template. Rendered content and templates made in PostHog (templateId) are the other two forms.Tracking: Silthus/posthog#476, spec Silthus/posthog#485 (section "Agent skill").
Source and changes: Silthus/posthog#511, spec Silthus/posthog#502 revision 4.
Flat send contract: PostHog/posthog#120921, slice 20 of the Resend parity spec (Silthus/posthog#544).
Impact
Pays into Grow Workflows through organizations that send email from code (not measured yet: it needs the send source on each send, PostHog/posthog#120628). In scope: transactional email. Expected move: medium, up. Run cost: n/a.
Merge Danger
Warning
🟡 Merge risk: medium · two-way door (merge-risk rubric)
/messaging/send/email/. Released before them, an agent writes calls that fail.https://posthog.com/docs/workflows/transactional-email-api.md, which answers404until PostHog/posthog.com#21306 publishes. Until then a clean release build fails for every skill.Summary
Adds the
send-transactional-emailskill next to theinstrument-*skills (context/skills/omnibus/send-transactional-email/). It ships the transactional email API docs page as its reference, and walks nine steps in order:integrations-list, and verify an unknown one once instead of guessing.success, bounded), or send the person to the PostHog UI when those tools are missing.templateId.subjectand anhtmlstring,{{ variables.* }}only, a key of letters, digits,-and_. The agent never uploads it: the first send does.templateKeyanswers404withnot_foundor no code.phs_key withhog_flow:write, read fromPOSTHOG_PROJECT_SECRET_API_KEY.appHostbehind a proxy,fromonly on a verified sender), or over HTTP to/messaging/send/email/with anIdempotency-Key. Over HTTP, a409 template_outdatedleads to an upload by key withif_code_managed: trueand a resend with the same key. Retries reuse the key; job runners passmaxRetries: 0and a key derived from the email's purpose.error.code, with the contract's codes and the409for a template edited in PostHog."Change an email the app already sends": the agent edits the local template in code. The first send with the changed content uploads it. A
template_supersededwarning points to itsfix."Inside the PostHog Wizard": questions go through
wizard_ask, the secret key goes to the env file throughset_env_valuesand never shows in the run, PostHog tools run throughposthog_exec, and a missing tool is inconclusive because the Wizard's login may lack their scopes. Two[ABORT]reasons match the abort cases of PostHog/wizard#1487.Evidence
Flat contract (slice 20), agent run on a fixture app. Claude Code on Sonnet 5.5 with only this skill, built from
c626ddfwith the docs page stubbed (it answers404until #21306 publishes). The app is the SvelteKit app of PostHog/wizard-workbench#2221. Prompt: add a welcome email after signup, with the MCP steps marked as done and no commands run.subjectandhtmlstrings that use{{ variables.* }}.templateFile,uploadTemplateortemplate:remains in the diff, and no MCP upload ran.templateFile()verbatim. No red agent run was made.Bundle (test seam: the context-mill build).
scripts/lib/tests/send-transactional-email-skill.test.jsis unchanged and passes at161a6fd.pnpm test: 29 files, 220 tests pass atc626ddf.Review. One GPT-6.1 Sol review round over this PR and #1487, then a final read. Three findings on this skill were fixed in
db4baf8and161a6fd: hidden tools inside the Wizard, the Wizard tool names, andreplyToneeding no verified sender.Gaps
Earlier evidence: the file-based shape (templateFile, MCP upload), up to 8b236c3
Bundle (test seam: the context-mill build).
scripts/lib/tests/send-transactional-email-skill.test.jsbuilds the skill throughgenerateSkillwithfetchstubbed for the docs URL, and assertsSKILL.mdis namedomnibus-send-transactional-email, listsreferences/transactional-email-api.md, and that file holds the docs page.AssertionError: expected undefined to be defined.shared_docsfails it.pnpm test: 29 files, 220 tests pass.pnpm buildemitsdist/skills/omnibus-send-transactional-email.zip(SKILL.md,references/transactional-email-api.md,references/COMMANDMENTS.md).Search ranking (test seam: the MCP
learn -sscorer). The PostHog MCP's ownSkillCatalog.searchResultsfromservices/mcp, over the releasedagent-skills-latestskills.zipwith this build's omnibus skills overlaid the way.github/actions/download-context-mill-skillsdoes:consuming-endpoints-from-client-code)signals-scout-workflows)designing-email-templates)designing-email-templates)designing-email-templates)Local agent session. Claude Code on Sonnet 5.5, a clean config, only this skill and a local PostHog MCP. Django and the CDP worker run from the server stack with the flag on, and the scratch Node app uses
posthog-nodefrom #5312. The project starts with no sender and the MCP has no sender tools, so this exercises the fallback path.Transcript excerpt, skill at 5a1f8e1
@posthog/corefrom npm) and the agent stopped after one failed send instead of retrying.Source and changes (#511), local agent sessions. Same stack as above, from
spec/code-managed-templates07d7e0e, with no CDP worker (nothing sends). Each run gets its own project withhello@example.comverified. The scratch repo is a monorepo with the app inapps/web, and itsoriginishttps://x-access-token:ghp_…@github.com/example/acme.gitwith a fake token. Red runs use the skill at965a656, green runs this branch.965a656)github.com/example/acme,apps/web/emails/welcome.html, HEADapps/webNo run called an update, patch or design tool. Sonnet 5.5 already did the right thing with the old wording, so this is a proof that the behavior holds, not a red-green test. The new text adds the running-copy warning and the undo rule.
Green at c128595: upload, early publish, undo, and the template API
source_repositoryholds a token, a user name or a scheme.8b236c3: the same five rank-1 results with the same scores. The skill's description did not change.pnpm test(220 passed) andpnpm buildpass at8b236c3. The bundle test needed no change: no test asserts the skill's wording.8b236c3, rewords two sentences after the green run and has its own final read.Gaps
learncatalog (skills.zip) carries no context-mill skill today,instrument-*included. The table shows the overlaid catalog that agent hosts load.pnpm security-scan:skillscould not triage locally (no LLM provider), so it flagged 413 matches across other skills. None is in this skill.workflows-request-email-template-change(#513) and Edit with your agent (#503) don't exist yet.Earlier summary
Adds the
send-transactional-emailskill next to theinstrument-*skills (context/skills/omnibus/send-transactional-email/). It ships the transactional email API docs page as its reference, and walks the spec's nine steps in order:integrations-list, and verify an unknown one once instead of guessing.success, bounded), or send the person to the PostHog UI when those tools are missing.<title>is the subject, values come from{{ variables.* }}, neverperson.*.workflows-upload-email-template, passingsource:repositoryfromgit remote get-url originashost/owner/namewith any token dropped,pathfrom the repository root, andreffromgit rev-parse HEAD. posthog-node fills the same fields on its own uploads.sender_unavailableleads to a verify, then Make default.404without a code or withnot_found.phs_key withhog_flow:write, read fromPOSTHOG_PROJECT_SECRET_API_KEY.posthog.emails.sendwithposthog.emails.templateFile(...)in Node.js (Next.js and edge covered), the send route by key plus an upload where the project deploys elsewhere. Sends are not idempotent: no retries, ask before any test send.error.code.A section, "Change an email the app already sends", covers later changes:
templateFile(), the first send from the changed code uploads it. The agent uploads early only when the person asks, and says every running copy sends the new version.workflows-request-email-template-changewhere the MCP has it.The MCP serves context-mill skills as resources. Agent hosts that overlay context-mill onto the posthog skills, like the Tasks sandbox and PostHog Desktop, see it as
send-transactional-email.Decisions
templateKeywithtemplateDatafor a local template; it replacestemplateFile(), thetemplatefield anddatasourcelink404shows the API is offEarlier decisions (file-based shape)
SkillCatalogover the overlaid catalog, because the MCPlearncatalog has no context-mill skillstemplateFile()change happens only when the person askssender_unavailableleads to a verify (which sets an unset default), then Make default: no MCP tool sets the default sender<title>, preheader and whole file, so the SDK's first send needs no re-uploadInvolvement: earlier work had two Opus 5.5 review rounds and six final reads. The slice 20 work had one GPT-6.1 Sol review round and one final read, with live agent proof captured on a fixture app.
Companion: PostHog/posthog#118927 reserves the name so a posthog skill cannot shadow it.
Written by Claude Code (Opus 5.5).
🤖 Generated with Claude Code