Pre-flight QA for an email campaign — merge tags, broken links, spam triggers, compliance, mobile rendering — before you hit send
Installation
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Catch the embarrassing stuff before it ships to your list. Runs a full pre-flight check on an email: merge tag fallbacks, broken links, spam triggers, CAN-SPAM / GDPR footers, mobile rendering, size limits, accessibility.
Requires: Cogny MCP for campaign-ID mode. Paste-HTML mode works without MCP (but without send-context checks).
Usage
/pre-send-qa <campaign-or-draft-id> — QA a specific campaign (or draft, for Get a Newsletter) in your ESP
/pre-send-qa — then paste the email HTML/MJML/text when prompted
Steps
1. Load the email
Mode A — Campaign ID (MCP). Detect which ESP is connected (check both namespaces: mcp__cogny__<svc>__* and mcp__<svc>__*) and use the right tool:
ESP
Fetch tool
What the ID refers to
Klaviyo
get_campaign
Campaign object
Mailchimp
tool_get_campaign
Campaign object
Rule
tool_get_campaign
Campaign object
Get a Newsletter — pre-send
tool_get_draft
A draft that hasn't been sent yet
Get a Newsletter — post-send forensics
tool_get_sent + tool_get_report
A sent item
For every ESP extract: subject, preheader, from name, from email, reply-to, HTML body, text body, target segment/list, scheduled send time. Also fetch the associated list/audience size so Section 2.8 has recipient count.
Get a Newsletter has no single "campaign" object. If the user passes an ID, try tool_get_draft first (pre-send QA is the common use), fall back to tool_get_sent if that 404s. If they want to QA a scheduled send, use tool_list_scheduled to find it.
Mode B — Paste:
Ask for subject line, preheader, from name/email, and body HTML. Accept multi-line paste.
Section 2.8 (send context) is skipped in paste mode — no recipient count, no segment, no schedule.
2. Run the checklist
Walk through every category. For each item, mark [PASS], [WARN], or [FAIL] and include the specific offending content.
All merge tags well-formed — balanced brackets, closed blocks
All merge tags have fallbacks — e.g., {{ first_name|default:"there" }} (Klaviyo), *|IFNOT:FNAME|*there*|ELSE:*|FNAME|**|END:IF|* (Mailchimp). Flag any token without a fallback as FAIL.
"Hi ," or "Hello ," anti-pattern — substring search for bare punctuation after a greeting. This is the #1 embarrassing merge-tag failure.
Dynamic content blocks closed — open {% if %} must have {% endif %}; same for loops
No raw tokens in subject line — e.g., subject reading literally "Hi {{ first_name }}"
2.3 Links
Extract all <a href="..."> from the HTML. For each:
URL is absolute — no href="/path" without domain; no href="#" unless intentional anchor
URL resolves — HEAD-check each unique URL (cap at 30 requests, dedupe). Flag non-200s.
No localhost / 127.0.0.1 / .local — dev leak
No staging/dev domains — flag *.staging.*, *.dev.*, stage., preprod.
UTM parameters present on all outbound links — warn on links missing utm_source/utm_medium/utm_campaign
UTM consistency — all links should share utm_campaign; flag divergence
Link count — report total. Warn if >20 (clutter). Warn if <1 (no CTA).
CTA destination consistency — multiple buttons labeled "Shop now" should generally point to the same URL
2.4 Images
All <img> have alt text — non-empty, descriptive
No pure-image email — flag if text-to-image ratio is <40% text. Hero-image-only emails trigger spam filters.
Image URLs are absolute + HTTPS
Image URLs aren't hotlinked from sketchy hosts — flag if images are on imgur, cloudinary demo, etc. instead of a brand CDN
2.5 Compliance
Unsubscribe link present — regex for unsubscribe, preferences, opt out
Unsubscribe link is visible — not display:none or font-size:1px
Physical postal address in footer — CAN-SPAM requirement. Regex for a street/city pattern or flag if missing.
List-Unsubscribe header — flag if ESP allows setting it but it's not configured (one-click unsubscribe is required by Gmail/Yahoo for bulk senders)
Preference center link — nice-to-have; warn if only unsubscribe exists
2.6 Spam triggers
Scan subject + preheader + body for classic triggers and report offenders:
Word-level: free, winner, congratulations, guaranteed, risk-free, act now, click here, limited time, no obligation, viagra (lol), make money, earn extra, $$$
Punctuation: more than one ! in subject, !!!, ???
Casing: ALL CAPS WORDS longer than 3 chars in subject
Symbols:$$$, ★, excessive emoji (>3)
HTML: hidden text (color matches background), tiny font, embedded JavaScript, background images on <body>
Output each offender with context:
🔴 Subject contains "!!!"
🟡 Body has 4 instances of ALL CAPS words: "LIMITED", "TODAY", "ACT NOW", "DON'T"
2.7 Rendering & size
HTML size < 102 KB — Gmail clips at 102 KB. Report actual size.
Single root <table> layout (desktop email standard) OR uses MJML — flag flex/grid as a deliverability risk
Inline CSS — style attributes vs <style> blocks. Many clients strip <head> styles.
Mobile viewport meta — <meta name="viewport" content="width=device-width">
Font stack has fallbacks — no font-family: "Custom Font"; without a web-safe fallback
No <script>, no <form>, no external fonts loaded via JS — all stripped by most clients
2.8 Send context (MCP mode only)
Target segment is populated — recipient count > 0
Audience isn't the entire list for a promotional send — flag "All Subscribers" when there's a segmented alternative
Scheduled time isn't 3am recipient-local — flag weird send hours
Not a duplicate send — check last 30 days for a campaign with the same subject going to the same segment
3. Report
Pre-send QA: <campaign name | "pasted email">
Recipient count: <N> Send ETA: <time or "not scheduled">
Overall: <READY TO SEND | FIX BEFORE SEND | CRITICAL ISSUES>
🔴 Critical (will break or embarrass)
— <specific issue with offending text / URL / line>
— ...
🟡 Warnings (fix if you have time)
— ...
🟢 Passed
— <count> checks passed
──
Recommended fix order:
1. ...
2. ...
Merge-tag fallback cheatsheet for <ESP>:
[ESP-specific syntax examples]
4. Finding (optional)
If critical issues are found and the campaign is scheduled to send soon, create a finding:
{"title":"Campaign '<name>' has critical QA issues — blocked send","body":"<enumerated issues>","action_type":"pre_send_fix","priority":"high"}
5. Do not auto-fix
Never modify the campaign automatically. Always present the diff and let the user approve each fix.
Notes
ESP-specific merge tag syntax:
Klaviyo:{{ first_name|default:"there" }}
Mailchimp:*|FNAME|* with *|IFNOT:FNAME|*there*|ELSE:*|FNAME|**|END:IF|*
Rule / Get a Newsletter:[Firstname] with platform-specific fallback syntax
Spam-trigger lists are heuristics; modern spam filters use ML + reputation. Treat warnings as "raise eyebrow, check reputation" not "instant spam".