Designing Email Templates
PostHogOfficialPublished by PostHog
- Skill
- Content generator
- Business and productivity
- MIT
- No issues found
AutoGineer summary
Written by AutoGineer from the skill’s own files, not by the author
This skill enables the creation, editing, and management of email templates within the PostHog ecosystem. It focuses on authoring email designs as JSON structures that are compatible with PostHog's visual editor, allowing for both programmatic generation and human-editable updates. The skill supports Liquid templating for dynamic content, image management via a media library, and specific configurations for click tracking and unsubscribe links.
What it does
- Creates and updates email templates using structured JSON design definitions.
- Integrates Liquid templating for dynamic personalization of email content.
- Manages image assets through a multi-step upload and retrieval process.
- Configures link tracking and opt-out mechanisms for email campaigns.
- Provides tools to convert existing HTML emails into native, editable block-based templates.
- Supports read-modify-write cycles for updating templates and workflow-specific email actions.
When to use it
- When you need to design a new email template for PostHog broadcasts or workflow actions.
- When updating existing email templates to reflect new content or design changes.
- When migrating email content from external platforms into the PostHog library.
- When configuring specific email behavior like custom click tracking or dynamic Liquid variables.
Advantages
- Ensures templates remain editable in the PostHog visual editor.
- Supports consistent branding through structured design and spacing rules.
- Allows for precise control over dynamic content using Liquid.
- Facilitates seamless integration between programmatic updates and manual visual editing.
Usage examples
- “Create a welcome email template for new signups.”
- The agent generates a JSON design structure with the subject line and body content, saves it as a new template, and provides the PostHog edit link.
- “Update the footer link in my existing 'Newsletter' template.”
- The agent fetches the current template design, modifies the specific block, updates the template, and renders a preview for verification.
Install
Download the skill as a ZIP:
https://autogineer.com/agent-skills/posthog/designing-email-templates/downloadUnzip it into the cross-client skills folder of your project:
mkdir -p .agents/skills && unzip designing-email-templates.zip -d .agents/skills
Documentation by PostHog
Designing email templates
Use this skill when creating or editing email templates for PostHog workflows — broadcast campaigns and function_email workflow actions send the rendered template.
How authoring works
You author the design JSON (content.email.design) and save it with workflows-create-email-template. The server renders the sent email from your design with the same renderer PostHog's visual editor uses, so the template opens as editable blocks for humans and sends exactly what the design describes. Schema, the native blocks and spacing rules every email follows, and a working example in references/unlayer-design-json.md.
When talking to the user, call it the template's design — the design document format is an internal implementation detail. Always share the template's _posthogUrl edit link in your reply after creating or updating, so the user can open it in PostHog directly.
Read references/design-guidelines.md before composing — it covers committing to a design direction, typography, color, and the patterns that make an email look designed rather than generated. For one fragment the block editor can't express, use an html-type content block inside the design.
Personalization with Liquid
Email content uses Liquid templating. Liquid tags pass through the renderer as plain text, so use them anywhere — block text, subject, links:
Hi {{ person.properties.first_name | default: 'there' }},Marketing emails must include an unsubscribe link — render it with the built-in variables:
<a href="{{ unsubscribe_url }}">Unsubscribe</a>({{ unsubscribe_url_one_click }} is also available for one-click list-unsubscribe flows.)
A template written for Customer.io can keep its {% unsubscribe_url %} tag, which renders the same link as {{ unsubscribe_url }}.
Output is HTML-escaped by default, so a value renders as text even when it contains markup:
Don't add
| escape. It escapes a second time, and&reaches the inbox as&.For HTML your own backend builds, such as a summary list, add
| raw:{{ variables.summary_html | raw }}. Never userawon text a person typed.Format money with
currency:{{ variables.amount | currency: "USD" }}renders$1,234.50. A value that isn't a number renders unchanged.
Click tracking and opt-out
Every link is automatically rewritten through a click-tracking redirect. This breaks mobile universal links / app deeplinks, which only resolve when the href stays on their own domain. To keep a link untracked, mark its anchor (use an html block) with clicktracking="off" or data-ph-no-track:
<a href="https://app.example.com/deeplink" data-ph-no-track>Open in app</a>The marker must be on the <a> tag itself, not a child element. Opted-out links get no click metrics.
Images
Call media-images-list with purpose="email" first — reuse an existing image (a logo, a header banner) instead of uploading a duplicate.
To add a new image, upload it with the presigned flow rather than embedding bytes in the design:
media-image-upload-startwith the file's name andpurpose="email"— returnsid,upload_url, andform_fields.From a shell, POST the local file to
upload_url:curl -X POST <upload_url> -F key=value... -F file=@/path/to/image.png(theform_fieldsfrom the response,filelast). Never base64-encode image bytes into a tool call — a flipped token corrupts the image.media-image-upload-completewith theid— returns the permanenturl.
Put that url in the image block's values.src.url (see references/unlayer-design-json.md). The block also takes values.src.width/height; the media tools don't return dimensions, so if you have shell access to the local file, measure it yourself rather than guessing.
Images must be under 4MB and decode as PNG, JPEG, GIF, WebP, AVIF or BMP.
Creating a template
Call workflows-create-email-template with:
{
"name": "Welcome email",
"description": "Sent to new signups on day 0",
"type": "email",
"content": {
"templating": "liquid",
"email": {
"subject": "Welcome to {{ person.properties.company | default: 'our product' }}",
"design": {
"counters": { "u_row": 1 },
"schemaVersion": 16,
"body": { "rows": ["…"], "values": { "preheaderText": "Three short steps to get you set up" } }
},
"text": "Plain-text fallback of the same message"
}
}
}subjectis required for email templates.Put the preview text that most inboxes show after the subject in the design's
body.values.preheaderText. The rendered html carries it, so it reaches the inbox on every send path. Leavecontent.email.preheaderunset, so the email carries one preview text.Always author the
designand omithtml- the server renders html from the design. Sending hand-written html without a design produces an email the visual editor can only show as one raw block.Always provide
textas a real plain-text rendering of the message - clients that block rich content show onlytext, so filler like "placeholder" reaches real inboxes, and a text part that doesn't match the html hurts deliverability.The tool result returns an edit link into the PostHog library.
After creating (or updating), call
workflows-show-email-template— it renders an inline preview so the user sees the result.
Payload mechanics
Pass the design directly in the tool call — no scratch files, no pre-validation subprocesses, no payload preview rounds. Liquid tags ({{ }}, {% %}), apostrophes, single quotes, and emoji are ordinary characters inside JSON strings; only standard JSON escaping applies. Never rewrite content to avoid them — converting Liquid's single quotes to double quotes inside markup attributes breaks the markup. If the tool call is rejected as malformed, fix the JSON escaping and resend the same content unchanged.
Converting an existing HTML email
To move an email from another platform, rebuild it from native blocks instead of pasting its HTML into one html block, so people can still edit it:
Each part of the source becomes the native block it shows: a
heading, atextblock per paragraph, abuttonper call to action, animageper picture. Keep the source's colors, font sizes, and padding on each block.Re-host every image through the media library (see Images). Put the file's real dimensions in
src.widthandsrc.height, and set the displayed width withsrc.maxWidthas a percentage of the content width. Source HTML often declares a height that doesn't match the file.A section the source wraps in
{% if %}becomes a block with a display condition (see references/unlayer-design-json.md).Rename the source platform's Liquid variables to the ones this send provides. A transactional API send provides everything the caller passes in
message_dataas{{ variables.* }}.Buttons have no
box-shadow. A 3px bottom border in a darker shade (border.borderBottomWidth,borderBottomStyle,borderBottomColor) gives the same raised look.The
socialblock draws only its own icon sets. To keep custom icons, put the icon row in anhtmlblock with the re-hosted images.
Editing a template (read–modify–write)
content is replaced as a whole on update, never merged — and humans may have edited the design in PostHog's visual editor since you last saw it:
workflows-get-email-template— always fetch fresh; the returneddesignis the current source of truth.Modify the
design(keep subject/text alongside it).workflows-update-email-template— send the completecontentback. The server re-renders the sent email from the edited design.workflows-show-email-template— render the updated template so the user sees the change; its response carries the final rendered html, so read it before describing the result.
For small changes to an existing design, prefer workflows-patch-email-template: id-addressed operations over the Unlayer blocks, so you send only the edit instead of the whole design.
Editing the email inside a workflow step
A function_email step carries its own email snapshot (config.inputs.email.value with subject/text/html/design), independent of any library template.
Edit it with workflows-patch-action-email: the same design operations as workflows-patch-email-template, plus an email_patch merge for subject, preheader, text, and recipients.
workflows-get— the step's current design (and its block ids) is inconfig.inputs.email.value.design.workflows-patch-action-emailwith the workflow id, the step'saction_id, and your operations and/oremail_patch. When the operations change the copy, the same call'semail_patchsets a matchingtext.The HTML is re-rendered server-side from the patched design, so it never goes stale.
On an active workflow the edit stages a draft — test with
workflows-test-run(use_draft=true) and apply it withworkflows-publish.
Using templates
List what exists with
workflows-list-email-templates(metadata only; fetch one for its content).When the user asks to see a template, call
workflows-show-email-template— it renders an inline preview.Reference a template from a workflow's
function_emailaction (its UUID inconfig.template_uuid), or start a broadcast from it in the PostHog UI. The step takes a snapshot of the template's body at save - editing the library template later does not change steps that already used it. To change a step's email, patch that step withworkflows-patch-action-email.Templates are soft-deleted by setting
deleted: trueviaworkflows-update-email-template.
Files
- ATTRIBUTION.mdAdded by AutoGineer869 B
- LICENSELicence file added by AutoGineer1.0 KiB
- SKILL.md10.1 KiB
- references/design-guidelines.md4.2 KiB
- references/unlayer-design-json.md11.8 KiB
references/design-guidelines.md
Email design guidelines
These guidelines adapt strong frontend design practice to the email medium — they apply to the Unlayer design as a whole and to the markup fragments inside its text blocks. The goal is a template that looks deliberately designed for the brand — not a generic notification.
Commit to a direction first
Before composing, decide:
Purpose and audience — transactional receipt, product announcement, win-back campaign, and onboarding emails each warrant different energy.
Tone — pick one and execute it precisely: brutally minimal, editorial/magazine, luxury/refined, playful, industrial/utilitarian. Intentionality beats intensity.
Brand — pull real colors, voice, and logo from the sender's product or site. If given a URL, mine it for the palette and typographic feel.
The memorable thing — one element the reader will remember: a bold header treatment, a striking stat, an unusual color block. One, not five.
Typography
Build hierarchy with size, weight, and color contrast, not font variety: one display treatment for the headline (large, tight line-height, heavy weight), one comfortable body style (15–17px, 1.5–1.6 line-height).
Web-safe stacks can still have character:
Georgia, 'Times New Roman', serifreads editorial;'Trebuchet MS', Tahoma, sans-serifreads friendly;'Courier New', monospacereads technical. Choose to match the tone instead of defaulting to Arial everywhere.Custom fonts via
<link>/@font-facerender in Apple Mail and partially elsewhere — use them as enhancement with a fallback stack that still fits the design.Constrain line length (~35em); let headlines breathe with padding above and below.
Color
Commit to a small palette: one dominant color, one sharp accent for the CTA, neutrals for text. Evenly-distributed timid palettes read as template-default; dominant-plus-accent reads as designed.
Use consistent hex values everywhere (no CSS variables in email — repeat the literal values; keep a comment block at the top of the document listing the palette so edits stay consistent).
Check contrast: body text ≥ 4.5:1 against its background. Test the design holds on both white and dark backgrounds — many clients force dark mode and invert naive black-on-white.
Layout and space
Asymmetry and overlap are mostly unavailable in table layout — get visual interest from generous, deliberate whitespace, full-bleed color sections, and strong alignment instead.
Vary section rhythm: a full-width color band for the header, padded white content sections, a tight dark footer. Uniform 20px-padding-everywhere is what makes emails look auto-generated.
One column. Side-by-side cells should be rare, content-justified, and must degrade acceptably when stacked.
Visual details that survive email clients
CTA buttons: a native
buttonblock with a solid background color, padding, and border-radius, so people edit its text and link in the editor (shape in unlayer-design-json.md).Solid
bgcolorsections, border accents (a 4px top border in the accent color is cheap and distinctive), and spacer rows are the reliable atmosphere tools. Gradients, background images, and shadows are enhancement-only — the design must work without them.A real text preheader (hidden with inline styles) controls the inbox preview line — write it like ad copy, don't let the client scrape your header nav.
What reads as machine-generated (avoid)
Centered white card on gray, purple gradient header, Arial everywhere at uniform sizes, three equal feature columns with stock icons, evenly-spread pastel palette, "Hi {name}," as the only personalization. If the draft resembles that, the direction wasn't committed to — restart from the tone decision, don't polish it.
Quality pass before saving
Read the design top to bottom: every block carries its spacing values (see unlayer-design-json.md), palette values consistent, alt text on images.
Squint test on the rendered preview: clear hierarchy — eye lands on headline → key message → CTA.
Confirm Liquid variables have
| default:fallbacks so no reader sees a blank.Confirm the plain-text version carries the full message, not a stub.
Requirements and compatibility
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta
“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.
Security
No issues found by our static scan· No issues found
A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.
Attribution and licence
- Published by
- PostHog
- Publisher
- PostHog (Official)
- Source
- PostHog/skills · skills/omnibus/designing-email-templates · commit a5d30e7
- Licence
- MIT License (redistribution allowed)
- LICENSE (added by AutoGineer from LICENSE)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
AutoGineer is not affiliated with or endorsed by PostHog or PostHog.
Download ZIP
The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download designing-email-templates.zip