← MailteaCONTENT HISTORY

Update to Mailtea

Snapshot Nov 6, 2026 · 07:26 UTC · version 0.10.0

Collection source: downloaded plugin package.

WHAT CHANGED

First saved snapshot

No earlier snapshot is available to establish a change.

Save Mailtea and check future changes in Following.

Up to 3 plugins free · no card required.

Compare saved observations

Download comparison JSON
Full technical diff · 0 changed fields
Full snapshot data
{
  "description": "Design a beautiful, on-brand, deliverable email, newsletter or template in Mailtea. Use when an agent is about to build or restyle a Mailtea email — covers the structured block/ops path (fastest, email-safe by construction), hand-written email-safe HTML when you own the whole document, images, brand colour, and the render/QA loop. Pairs with the mailtea-email skill (which covers sending/managing) — this one covers how the email should LOOK.",
  "included_files": [
    {
      "relative_path": "agents/openai.yaml",
      "size_in_bytes": 364
    },
    {
      "relative_path": "assets/mailtea-mark.svg",
      "size_in_bytes": 980
    }
  ],
  "name": "mailtea-email-design",
  "skill_md_contents": "---\nname: mailtea-email-design\ndescription: Design a beautiful, on-brand, deliverable email, newsletter or template in Mailtea. Use when an agent is about to build or restyle a Mailtea email — covers the structured block/ops path (fastest, email-safe by construction), hand-written email-safe HTML when you own the whole document, images, brand colour, and the render/QA loop. Pairs with the mailtea-email skill (which covers sending/managing) — this one covers how the email should LOOK.\n---\n\n# Mailtea email design\n\n`mailtea-email` teaches you how to *send*. This teaches you how to make the email\n*look good* — and survive real inboxes. Email is not the web: no flexbox, no CSS\nvariables, no web fonts you can rely on, and Outlook renders with Word.\n\n## Choose your path first — one of these is much faster\n\n| | **A. Ops (structured blocks)** — prefer this | **B. Hand-written HTML** |\n|---|---|---|\n| You write | Declarative blocks: `{kind:\"heading\",…}`, `{kind:\"button\",…}` | Tables, inline styles, media queries |\n| Email-safety | **By construction.** The reducer only emits nodes that survive an inbox; it cannot produce `display:flex` | **Yours to get right** — see the contract below |\n| Editing later | Surgical: address one block by path and change it | Re-emit the whole document |\n| The operator | Keeps editing it as normal blocks in the Visual Email Designer | Opens as one \"Custom HTML\" block |\n| Use for | Newsletters, broadcasts, anything an operator will touch again | Pixel-controlled transactional designs (receipts) where YOU own the whole layout |\n\n**Default to A.** Reach for B only when you genuinely need a complete\n`<!DOCTYPE html>` document. If you are doing B, skip to *The email-safe contract*.\n\n## A. The ops path\n\n### The loop\n\n```\nissue.create_draft  →  issue.apply_ops (compose + set_styles + set_headers)\n                    →  email.lint      →  issue.preview / posts send-test\n                    →  issue.apply_ops (edit_text / edit_block / arrange) to refine\n```\n\n`issue.apply_ops` takes a batch and returns a **report**: `applied`, `skipped`\n(each with a reason), and — after any structural op — a fresh **outline**. Read\nthe report. It is the correction signal; nothing fails silently.\n\n### Address blocks by path, from the outline\n\nPaths are dot-joined child indexes: `0.3`, `0.12`. **Always read the outline from\nthe last response (or `issue.get_editor`) before addressing anything.** Never\ncarry paths across turns without re-reading — the tree moves when blocks are\nadded or removed.\n\n```bash\nmailtea call issue.get_editor --json '{\"issueId\":\"iss_…\"}'   # → outline, styles, headers\n```\n\n### Ops\n\n| op | what it does |\n|---|---|\n| `compose` | Replaces the whole body. Establishes the editor's root wrapper, so paths start `0.0`, `0.1`, … and stay put. |\n| `insert_blocks` | Adds blocks at `path` + `position` (`before`/`after`/`append-into`); no path = append to the end. |\n| `edit_text` | Rewrites one block's copy: `{edits:[{path,text}]}`. |\n| `edit_block` | Sets one block's attributes: `{path, expectType, attrs}`. |\n| `set_styles` | The email's look, as flat tokens (below). |\n| `arrange` | `moves` then `deletes`. Addresses resolve against the doc as it stands when this op runs, so an earlier `insert_blocks` or `arrange` in the same batch has already shifted them. Give every delete an `expectType` (`{path, expectType}`): if any address in the op is stale, the whole op is refused and nothing is deleted. |\n| `set_headers` | `subject` and `previewText` (the inbox preheader). |\n\n### Block kinds\n\n`heading` (text, level 1-3) · `text` · `bulletList` / `numberedList` (items) ·\n`quote` · `button` (label, href, alignment, variant) · `image` (src, alt,\nalignment) · `divider` · `spacer` (height) · `columns` · `section` · `footer` ·\n`html` · `variable` (key, fallback).\n\nText fields take inline markup — `**bold**`, `*italic*`, `[label](https://url)` —\nand nothing else. **A `\\n` inside one text block becomes a line break**; for two\nvisually separate lines with normal spacing, use two blocks.\n\n### Where a block appears (`visibility`)\n\nAny block can carry `visibility`: `all` (default), `email`, or `web`. Footers\nand logos default to `email` (the site has its own); give one\n`visibility: \"all\"` to keep it on the web page. Set it with\n`edit_block` `attrs:{visibility}`, or as `visibility` on a block in `compose` or\n`insert_blocks`. The outline shows it when it is not `all`. Check each version\nwith `issue.preview` and `channel: \"email\"` or `\"web\"`. A post where every block\nis `web` has an empty email: the send is accepted, then the post is marked failed\nwith the reason `email_channel_empty` (in `issue.delivery_progress` and\n`issue.wait_delivery`). Studio's Review warns first.\n\n### Brand colour — `set_styles` tokens\n\n```json\n{\"op\":\"set_styles\",\"tokens\":{\n  \"accentColor\":\"#FF6719\", \"linkColor\":\"#B23C00\", \"headingColor\":\"#1a1a1a\",\n  \"textOnAccentColor\":\"#ffffff\", \"linkDecoration\":\"underline\",\n  \"bodyFontSizePx\":\"18\", \"titleFontSizePx\":\"34\",\n  \"bodyText\":\"#1a1a1a\", \"pageBackground\":\"#ffffff\", \"bodyBackground\":\"#ffffff\",\n  \"fontFamily\":\"Georgia, 'Times New Roman', Times, serif\",\n  \"bodyWidth\":\"600\", \"bodyPadding\":\"24\", \"pagePadding\":\"16\",\n  \"bodyCornerRadius\":\"0\", \"bodyBorder\":\"0\", \"bodyBorderColor\":\"#e5e7eb\"\n}}\n```\n\n`accentColor` drives the button fill, the quote rule and section badges;\n`linkColor` drives body links. **They are not the same value.** An accent chosen\nto look right as a button fill is usually too light as link text — check the link\ncolour reaches 4.5:1 against the body background and darken it if not.\n\nA button that was given its own colour keeps it and ignores the accent. To\nrecolour that one button: `edit_block` with `attrs:{buttonColor, textColor}`.\n\n`titleFontSizePx` sets the whole heading scale — h2 and h3 derive from it, so\nthere is no separate token per level. Sizes are range-checked, not clamped: ask\nfor 9px body copy and the op is refused with the range, because a size nobody\nchose is worse than an error.\n\n**Underline your links unless something else distinguishes them.** The default\nis `none` (it matches the template corpus), but colour alone fails WCAG 1.4.1 —\n`\"linkDecoration\":\"underline\"` is the fix.\n\n### Images\n\nAn image block needs an **absolute URL**, so the picture has to exist somewhere\nfirst. Upload it — do not hot-link somebody else's host, and do not invent a URL.\n\n```bash\nmailtea assets upload --publication-id pub_123 --file ./hero.png \\\n  --name hero.png --width 1200 --height 452     # → JSON with the permanent `url`\nmailtea assets list --publication-id pub_123    # what is already there\n```\n\nThen place it, with real alt text:\n\n```json\n{\"op\":\"insert_blocks\",\"path\":\"0.5\",\"position\":\"after\",\"blocks\":[\n  {\"kind\":\"image\",\"src\":\"https://…/asset_….png\",\"alt\":\"What the picture shows.\",\"alignment\":\"center\"}]}\n```\n\nFour rules that decide whether the image works:\n\n- **PNG or JPEG for email; 5 MB max.** GIF and WebP upload too. SVG uploads, but\n  Gmail and Outlook do not show SVG images in email, so never put one in an\n  email or template. The bytes are checked against the declared type, so a\n  mislabelled file is rejected, not stored.\n- **Size the artwork for a phone.** A 1200px-wide image lands at roughly 350px in\n  a phone's mail app. Type drawn smaller than ~40px in that artwork arrives under\n  12px and stops being readable. Design for the phone, then check it.\n- **Never put meaning only in an image.** Many clients block images by default —\n  the email must still read with them off, which is what `alt` is for.\n- **Retiring an asset does not remove it from the email.** `assets delete` hides\n  it from the library and the file keeps resolving (so already-sent mail stays\n  intact); repoint or delete the block yourself.\n\n### Gotchas that cost real time\n\n- **`compose` replaces the body.** To add to an existing email use\n  `insert_blocks`.\n- **An HTML-backed draft cannot be addressed by ops.** `issue.get_editor` reports\n  `docBacked: false` for those; `compose` first, then ops work.\n- **The subject IS the issue title.** `set_headers` moves it. If an operator has\n  the email open in the editor, refresh their tab after changing it.\n- **Lint before you send, every time.** `email.lint` is a few seconds and it\n  names the client that breaks.\n\n## B. Hand-written HTML\n\n### 0. Pick the surface\n\nMailtea has two content models. Using the wrong one is the #1 cause of \"it looked\ndifferent when sent / when I opened it in the editor.\"\n\n| | **Complete document** | **Newsletter fragment** |\n|---|---|---|\n| What it is | A whole self-contained email: `<!DOCTYPE html><html><head>…fonts/styles…</head><body>…own background/footer…</body>` | BODY content only — no `<html>`/`<head>`/`<body>`, no outer background, no footer |\n| Ships via | **Transactional** — `POST /v1/emails` (html or template). Delivered **verbatim** (sanitize + tracking only). | **Newsletter/broadcast post** — `POST /v1/posts`. Mailtea **wraps it** in the publication's email shell (`<Container>` + unsubscribe footer). |\n| Use for | Pixel-controlled designs, receipts, review requests, anything where YOU own the whole layout. | Regular editions where Mailtea's shell + compliant unsubscribe footer is wanted. |\n| No-code editor | Opens **as one faithful \"Custom HTML\" block** — preview == inbox; edit the raw HTML. Block editing is intentionally NOT offered (it can't change a complete document without losing it). | Maps cleanly to editor **blocks** (text, image, button…); the shell is added at send. |\n\nRules of thumb:\n- **A complete `<!DOCTYPE html>` document → send it transactionally.** If you post it as a\n  newsletter it gets double-wrapped (a full `<html>` nested inside Mailtea's shell) and\n  the sent email won't match your design.\n- **Writing a newsletter post → emit a body fragment**, not a full document. Don't include\n  `<html>`/`<head>`/`<body>`, your own page background, or an unsubscribe footer — Mailtea\n  supplies the shell and a compliant footer. Fragments also survive the block editor.\n- Either way, opening custom HTML in the no-code editor converts it into an **editable\n  block-based design** (high-fidelity — tables/text/images/buttons become real blocks, the\n  page background/width carry over), so operators can keep editing what you produced.\n\n### 1. Frame (shape)\n\nBefore any HTML, decide:\n\n- **Type** — transactional (one-shot, e.g. receipt/review-request) or marketing\n  (newsletter/broadcast). Transactional leans focused + single-action; marketing\n  can carry more sections.\n- **One job per email.** A single primary action. Everything else is support.\n- **Variables** — what's personalized (`{{first_name}}`, `{{review_url}}`,\n  `{{unsubscribe_url}}`). Plan these as `{{snake_case}}` tokens now.\n\n### 2. The email-safe contract (non-negotiable)\n\nThese aren't style preferences; break them and the layout collapses in a major client.\n\n- **Tables, not divs, for layout.** `<table role=\"presentation\" cellpadding=\"0\"\n  cellspacing=\"0\">`. No `<div>` grids.\n- **Inline every style** (`style=\"…\"` on the element). `<style>` in `<head>` is\n  for `@media` and `:hover` only — Gmail strips much of it.\n- **Fixed width ~600px**, centered, with a `@media (max-width:620px){ width:100%\n  !important }` fallback for mobile.\n- **Never emit these** — Mailtea lints serialized HTML against the real\n  caniemail matrix (`email.lint`), strict clients = **Apple Mail, Gmail,\n  Outlook desktop**:\n\n  | Severity | Don't use | Use instead |\n  |----------|-----------|-------------|\n  | **fail** (breaks) | `display:flex` / `inline-flex` | nested tables / `align`, `valign` |\n  | **fail** | `position:absolute` / `fixed` | document flow only |\n  | **fail** | `gap` | cell `padding` / spacer rows |\n  | **fail** | `transform` | pre-positioned markup; a pre-rendered **image** for arched/rotated wordmarks |\n  | **fail** | `aspect-ratio` | explicit `width`/`height` |\n  | **fail** | `clamp()` | fixed px + a `@media` override |\n  | **fail** | `vh` / `vw` units | `px` / `%` |\n  | **fail** | `var(--…)` | literal values inline |\n  | **warn** (degrades) | `box-shadow`, `*-gradient()` | flat fills; treat as enhancement only |\n\n- **Fonts: declare a web font, always supply a system fallback.** Web fonts load\n  only in Apple Mail / some clients; everywhere else the stack must still look\n  intentional. `font-family:'Source Serif 4', Georgia, 'Times New Roman', serif`.\n- **Logos / wordmarks → a hosted image, NOT inline `<svg>`.** Gmail and Outlook strip\n  inline SVG entirely (it renders only in Apple Mail and in-app previews), so a `<textPath>`\n  arched wordmark vanishes for most recipients. Pre-render it to a PNG/SVG file and reference\n  it with `<img src=\"…\" alt=\"…\">`. (Inline SVG is also dropped when the email is opened in the\n  no-code editor, for the same reason.)\n- **Social / brand icons → hosted `<img>`, never icon fonts or bare glyphs.** A footer\n  row of `f  𝕏  in` typed as font-glyphs or characters renders as plain text in most\n  clients (no icon), looking broken. Each icon is a small hosted PNG/SVG\n  `<img width height alt style=\"display:block;border:0\">` inside its own cell — see the\n  social-row recipe below.\n- **Brand-critical display/poster type → a hosted image when the weight/tracking carries\n  the brand.** A heavy condensed poster headline (e.g. a giant `BIG COZY`) falls back to a\n  generic bold across clients and loses its punch. If the exact face matters, pre-render the\n  headline to an `<img>` (as with arched wordmarks); otherwise pick the closest web-safe\n  stack and accept the fallback.\n- **Images** need `width`, `height`, `alt`, and `style=\"display:block;border:0\"`.\n  Don't depend on them: many clients block images by default, so the email must\n  read with images off. Add a hidden **preheader** span as the first body node.\n- **Dark mode**: Apple Mail/Outlook may invert. Keep the email canvas warm/light\n  by design; don't rely on a dark background you didn't set.\n- **Accessibility**: real text (not text-in-images), `lang` on `<html>`,\n  `role=\"presentation\"` on layout tables, ≥14px body, AA contrast.\n\n## Design judgment (both paths)\n\nConstraints handled — now make it *good*. Follow the publication's own brand; if\nit hasn't got one written down, default to **precise, calm, recipient-faithful**.\n\nBelow is the rubric Mailtea's own email assistant is given and its eval harness\nscores against, so it is the bar your email is measured at too.\n\n<!-- BEGIN generated:design-rubric -->\n<!-- Generated from @mailtea/contracts by scripts/sync-rubric-docs.mjs.\n     Edit packages/contracts/src/design-rubric.ts, then run `pnpm rubric:sync`. -->\n\nEvery email you produce is scored on six dimensions, 0-4 each, 24 total. Aim for 4s.\n\n### Hierarchy\n\nOne dominant idea leads and everything else supports it. Composition carries the reading order before a word is read: with detail blurred, the primary element, the secondary element and the major groups should still be identifiable in that order. Rhythm comes from deliberate contrast between tight and generous intervals, not one spacing value repeated until every block weighs the same. Group by proximity before reaching for a box, and give a heading more room above it than below.\n\n4 — One unmistakable lead, groups that read in order, and an authored tight/generous rhythm.\n2 — A lead exists but competes with a second element, or spacing is uniform throughout.\n0 — No lead element — blocks of equal weight in an undifferentiated stack.\n\n### Typography\n\nScale, weight and measure are chosen rather than inherited. Heading, body and fine print are distinguishable at a glance, and no two sizes sit close enough to be doing the same job. Body copy stays comfortable — 16px and up, with a line length in the 45-75 character range — and the family belongs to the product instead of being the closest available default.\n\n4 — A deliberate role scale with obvious steps; body copy comfortable at every width.\n2 — A workable scale with one collision, or a measure that runs long.\n0 — Arbitrary or colliding sizes; body copy too small or too wide to read comfortably.\n\n### Color\n\nA restrained palette with roles, not a bag of swatches: a background, a surface, text, and one accent that owns the one action. Where the publication's design brief is present it governs — follow it strictly, and fall back to Mailtea house style only when no brief exists. The strongest color earns a deliberate region or role; spending it on decoration leaves nothing to mark the thing that matters.\n\n4 — Every color has a role, the accent is rare and lands on the action, and the brief is honoured.\n2 — Mostly coherent, but a second accent or a decorative color dilutes the action.\n0 — Palette sprawl or an accent scattered everywhere; a brief, if present, is contradicted.\n\n### Accessibility\n\nWCAG AA, measured rather than claimed. Body text clears 4.5:1 against what sits behind it and large text 3:1. Every image carries alt text that reads on its own. Headings descend without skipping a level, links are told apart by more than color, and nothing that matters exists only inside a picture.\n\n4 — Every measured pair passes, alt text is meaningful, structure is semantic.\n2 — Mostly compliant with one measurable failure — a low-contrast secondary, a missing alt.\n0 — Contrast failures on body copy, images without alt text, meaning carried only by an image.\n\n### Clarity\n\nCopy is concrete, active-voice and sentence case, in the publication's own language. Controls name the action they perform — a label reading \"Learn more\" out of context names nothing. There is one obvious next action, reachable without hunting. No hype adjectives, no exclamation marks, no invented statistics or testimonials.\n\n4 — Every line concrete, one named primary action, nothing overstated.\n2 — Readable copy, but a vague control label or a second action competing with the first.\n0 — Hype copy, generic labels, and no discernible next action.\n\n### Inbox fitness\n\nThe email survives a real client. It is a body FRAGMENT — never a full document, its own page background, or an unsubscribe footer, all three of which Mailtea's shell supplies. One column at around 600px, stacking cleanly on a phone. It still reads with images off, because many clients block them by default. Subject and preview text do different jobs and neither is empty. Shell chrome and the unsubscribe footer are outside this score.\n\n4 — Fragment, single column at inbox width, meaningful with images off, headers doing two jobs.\n2 — Deliverable but off-width, or the preview text merely restates the subject.\n0 — A full document, content that collapses with images off, or empty headers.\n\n### Named failure modes\n\nEach of these is a defect the score looks for by name. Do not produce one.\n\n- any.hierarchy.section-sprawl (P2) — Sections that do not earn their place — six things said vaguely instead of one thing said well.\n- any.hierarchy.weak-opening (P2) — No dominant opening: nothing establishes what this is before the reader has to work it out.\n- any.hierarchy.unauthored-rhythm (P2) — One spacing value repeated end to end — no tight/generous contrast, so every block weighs the same and nothing groups.\n- any.hierarchy.no-lead-among-peers (P2) — Several topics set at identical weight, leaving the reader to choose the lead the composition should have named.\n- any.typography.body-copy-too-small (P2) — Body copy set below 16px, which reads cramped on a phone.\n- any.typography.scale-collision (P2) — Heading and body sizes sit too close to carry different jobs — one size doing two jobs.\n- any.color.palette-sprawl (P2) — More colors than roles — beyond a background, a surface, text and one accent.\n- any.color.brief-violation (P1) — Contradicts the publication's design brief, which governs whenever one is present.\n- any.a11y.low-contrast (P1) — A text/background pair below WCAG AA — 4.5:1 for body copy, 3:1 for large text.\n- any.a11y.missing-alt (P1) — An image with no alt text, or alt text that does not read on its own.\n- any.a11y.meaning-in-image (P1) — Copy that exists only inside a picture — it disappears when images are blocked or unreadable.\n- any.clarity.hype-copy (P2) — Hype adjectives, exclamation marks, or invented statistics and testimonials.\n- any.clarity.generic-cta (P2) — A control labelled \"Click here\" or \"Learn more\" — a label that names nothing out of context.\n- any.clarity.no-primary-action (P1) — No obvious next action, or several competing for the same attention.\n- email.inbox.full-document (P0) — Emitted a full HTML document, its own page background, or an unsubscribe footer — Mailtea's shell supplies all three, so this double-wraps.\n- email.inbox.missing-headers (P1) — Subject line or preview text left empty after composing.\n- email.inbox.subject-preview-echo (P2) — Preview text restates the subject instead of saying what the subject left out.\n- email.inbox.body-width-off (P2) — Body width outside 480-680px, the range inboxes render predictably.\n- email.inbox.image-off-collapse (P1) — The email stops making sense with images blocked, which is how many clients open it.\n- email.color.accent-two-jobs (P2) — Headings drawn in the accent, so they compete with the button for the same attention.\n- email.a11y.link-color-only (P2) — Body links told apart by color alone and below 3:1 against the surrounding text — WCAG 1.4.1.\n\n<!-- END generated:design-rubric -->\n\n### Mailtea house specifics\n\n- **typeset** — One serif for body (`Source Serif 4`), one sans for UI/labels\n  (`Space Grotesk`); `Instrument Serif` for display moments. Body 16–19px,\n  line-height ~1.6–1.75, generous paragraph spacing (margin, since `gap` is out).\n  Don't mix four fonts.\n- **layout** — Vertical rhythm via consistent cell padding. \"One column\" governs the\n  *outer* frame — but faithful designs often have multi-column *regions* (a 2-up product\n  grid, an image-beside-text row, a card collage). Build those as nested tables with\n  fixed-percentage cells (`<td width=\"50%\">`), and add a `@media (max-width:620px)` rule\n  that stacks them (`display:block;width:100%`) on mobile. Don't flatten a genuine\n  side-by-side layout into a single stacked column. Let the white canvas breathe; hairline\n  dividers over boxes.\n- **colorize** — Near-neutral text on a warm/white canvas. Status meaning stays\n  (scheduled blue, sent green, failed red). No garish gradients (and they're\n  `warn`-listed anyway).\n- **polish** — The last 10%: optical alignment, consistent corner radii\n  (`rounded-md`/`-lg`, not pill-everything), an underlined text link with\n  `text-underline-offset` instead of a heavy button when the tone is personal,\n  a real signature, a complete footer (address + unsubscribe).\n\n## Render and compare — never ship unrendered\n\nNever ship unrendered. The loop:\n\n1. **Lint** — run the HTML past `email.lint`; resolve every `fail`.\n2. **Render with variables** — Mailtea substitutes `{{var}}` and `{{{var}}}`\n   placeholders. On the **posts** path only *passed* variables are replaced, so\n   pass them all; the **transactional** send path also applies a variable's\n   declared `fallback_value`.\n3. **See it in a real client** — `issue.preview`, a send-test to your own\n   address, or open the rendered HTML in a browser.\n4. **Compare to intent** — if you were given a reference, screenshot the render and\n   check fidelity (wordmark, fonts, spacing, color, footer). Fix the gaps, re-render.\n\n## Create it in Mailtea\n\nOnce it renders right, persist it as a reusable **server template** (then any\nsend can reference it by id + variables):\n\n```bash\n# MCP (no code): tools template.create → template.publish\n# CLI:\nmailtea templates create --publication-id pub_123 --name \"Review request\" \\\n  --subject \"A small favor\" --html \"$(cat email.html)\"\nmailtea templates publish <template-id> --publication-id pub_123\n\n# Use it (substitutes server-side):\nmailtea posts create --publication-id pub_123 --subject \"A small favor\" \\\n  --template-id <template-id> \\\n  --template-var first_name=Friend --template-var review_url=https://…\n```\n\nDeclare each `{{variable}}` (with `type`, optional `fallback_value`) on create so\nthe template is self-documenting. REST equivalents: `POST /v1/templates`,\n`POST /v1/templates/:id/publish?publication_id=…`, `POST /v1/posts`. For transactional\nsends, pass a `template` object on `POST /v1/emails` instead — see `mailtea-email`.\n\n## Starter skeleton (correct by construction)\n\nA 600px card that satisfies the contract. Start here, then design into it.\n\n```html\n<!DOCTYPE html>\n<html lang=\"en\"><head>\n<meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n<meta name=\"x-apple-disable-message-reformatting\">\n<style>\n  body{margin:0;padding:0;background:#efeeec}\n  table{border-collapse:collapse}\n  img{border:0;line-height:100%;outline:none;text-decoration:none}\n  @media (max-width:620px){.card{width:100%!important}.pad{padding-left:28px!important;padding-right:28px!important}}\n</style></head>\n<body>\n  <span style=\"display:none;visibility:hidden;opacity:0;height:0;width:0;font-size:1px;color:#efeeec;\">Preheader — the inbox preview line.</span>\n  <table role=\"presentation\" width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" style=\"background:#efeeec\">\n    <tr><td align=\"center\" style=\"padding:36px 16px\">\n      <table role=\"presentation\" class=\"card\" width=\"600\" cellpadding=\"0\" cellspacing=\"0\" style=\"width:600px;max-width:600px;background:#fff\">\n        <tr><td class=\"pad\" style=\"padding:48px 56px;font-family:'Source Serif 4',Georgia,serif;font-size:18px;line-height:1.72;color:#1b1b1b\">\n          <p style=\"margin:0 0 24px\">Hi {{first_name}}!</p>\n          <p style=\"margin:0 0 24px\">Your message. One clear idea.</p>\n          <p style=\"margin:0\"><a href=\"{{cta_url}}\" style=\"color:#1b1b1b;text-decoration:underline;text-underline-offset:3px\">One clear action →</a></p>\n        </td></tr>\n        <tr><td align=\"center\" class=\"pad\" style=\"padding:18px 56px 48px;font-family:'Source Serif 4',Georgia,serif;font-size:13px;color:#9b9b9b\">\n          Your Company · City, Country<br>\n          <a href=\"{{unsubscribe_url}}\" style=\"color:#b4b4b4;text-decoration:none\">Unsubscribe</a>\n        </td></tr>\n      </table>\n    </td></tr>\n  </table>\n</body></html>\n```\n\n## Recipes (copy these shapes for faithful recreations)\n\nThese are the three shapes agents most often get wrong when reproducing a real design.\nAll survive the email-safe lint AND the no-code editor's HTML→blocks import.\n\n**Social icon row** — hosted images in cells, never glyphs:\n\n```html\n<table role=\"presentation\" cellpadding=\"0\" cellspacing=\"0\" align=\"center\"><tr>\n  <td style=\"padding:0 8px\"><a href=\"{{facebook_url}}\"><img src=\"https://cdn.example.com/ic-facebook.png\" width=\"28\" height=\"28\" alt=\"Facebook\" style=\"display:block;border:0\"></a></td>\n  <td style=\"padding:0 8px\"><a href=\"{{instagram_url}}\"><img src=\"https://cdn.example.com/ic-instagram.png\" width=\"28\" height=\"28\" alt=\"Instagram\" style=\"display:block;border:0\"></a></td>\n  <!-- one <td> per icon -->\n</tr></table>\n```\n\n**Two-column region** — nested table with `%` cells + a mobile stack fallback (put the\n`@media` rule in `<head>`; give each column class `col`):\n\n```html\n<style>@media (max-width:620px){.col{display:block!important;width:100%!important}}</style>\n…\n<table role=\"presentation\" width=\"100%\" cellpadding=\"0\" cellspacing=\"0\"><tr>\n  <td class=\"col\" width=\"50%\" valign=\"top\" style=\"padding:12px\">…left…</td>\n  <td class=\"col\" width=\"50%\" valign=\"top\" style=\"padding:12px\">…right…</td>\n</tr></table>\n```\n\n**Card with chrome** — accent bar as a colored row, outline + radius inline on the cell\n(inline `border`, `border-radius`, `background` all survive the editor import; the bare\n`border`/`bordercolor` HTML attributes are also honored):\n\n```html\n<table role=\"presentation\" width=\"100%\" cellpadding=\"0\" cellspacing=\"0\"\n       style=\"border:1px solid #e5e7eb;border-radius:8px\">\n  <tr><td style=\"height:6px;background:#2680eb;border-radius:8px 8px 0 0;font-size:0;line-height:0\">&nbsp;</td></tr>\n  <tr><td style=\"padding:0\"><img src=\"…\" width=\"252\" height=\"160\" alt=\"…\" style=\"display:block;border:0;width:100%\"></td></tr>\n  <tr><td style=\"padding:14px 16px;font-weight:600;text-decoration:underline\">Card label</td></tr>\n</table>\n<!-- pill/badge: bg + radius + padding on an inline-block-ish cell -->\n<span style=\"display:inline-block;padding:4px 12px;background:#111;color:#fff;border-radius:999px;font-size:12px\">New</span>\n```\n\n## Don't\n\n- Don't reach for hand-written HTML when blocks would do — the ops path is\n  email-safe by construction and stays editable for the operator.\n- Don't reuse a block path from an earlier turn. Re-read the outline.\n- Don't put an image in without `alt`, or size its type for a desktop preview.\n- Don't paste a web/marketing-page layout into an email — flex/grid/`var()` will\n  collapse in Outlook and Gmail.\n- Don't type social icons as font glyphs or characters — use hosted `<img>` icons.\n- Don't flatten genuine side-by-side layouts into one stacked column — use a `%`-cell\n  nested table with a `@media` mobile stack.\n- Don't ship without rendering in a client.\n- Don't leave `{{placeholders}}` unsubstituted, or a footer without an unsubscribe link.\n- Don't reach for `box-shadow`/gradients/buttons by reflex; flat and typographic\n  usually reads calmer and survives more clients.\n"
}

SHA-256 of public snapshot: 2b2d31965d69b9e9064e5083d28e58aa54492047ab5c7b678409cad8a1c5a493