{"id":"39519b9d-3853-4ad0-8959-2d312d1dfce5","shortId":"qj4Byb","kind":"skill","title":"pre-send-qa","tagline":"Pre-flight QA for an email campaign — merge tags, broken links, spam triggers, compliance, mobile rendering — before you hit send","description":"# Pre-Send QA\n\nCatch 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.\n\n**Requires:** Cogny MCP for campaign-ID mode. Paste-HTML mode works without MCP (but without send-context checks).\n\n## Usage\n\n`/pre-send-qa <campaign-or-draft-id>` — QA a specific campaign (or draft, for Get a Newsletter) in your ESP\n`/pre-send-qa` — then paste the email HTML/MJML/text when prompted\n\n## Steps\n\n### 1. Load the email\n\n**Mode A — Campaign ID (MCP).** Detect which ESP is connected (check both namespaces: `mcp__cogny__<svc>__*` and `mcp__<svc>__*`) and use the right tool:\n\n| ESP | Fetch tool | What the ID refers to |\n|-----|------------|----------------------|\n| Klaviyo | `get_campaign` | Campaign object |\n| Mailchimp | `tool_get_campaign` | Campaign object |\n| Rule | `tool_get_campaign` | Campaign object |\n| Get a Newsletter — pre-send | `tool_get_draft` | A draft that hasn't been sent yet |\n| Get a Newsletter — post-send forensics | `tool_get_sent` + `tool_get_report` | A sent item |\n\nFor 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.\n\n**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.\n\n**Mode B — Paste:**\n- Ask for subject line, preheader, from name/email, and body HTML. Accept multi-line paste.\n- Section 2.8 (send context) is skipped in paste mode — no recipient count, no segment, no schedule.\n\n### 2. Run the checklist\n\nWalk through every category. For each item, mark `[PASS]`, `[WARN]`, or `[FAIL]` and include the specific offending content.\n\n#### 2.1 Headers & routing\n- [ ] **Subject line present** — not empty\n- [ ] **Subject length ≤ 55 chars** (mobile truncation threshold)\n- [ ] **Preheader present** — not empty, not a duplicate of subject\n- [ ] **Preheader length 85–100 chars** (too short = wasted real estate; too long = truncated)\n- [ ] **From name is branded** — not \"noreply\", not \"info\"\n- [ ] **From email uses sending subdomain** — flag `@gmail.com`/`@outlook.com` from addresses (deliverability killer for bulk)\n- [ ] **Reply-to is monitored** — warn if it's a noreply@\n\n#### 2.2 Merge tags / personalization\nScan body + subject + preheader for:\n- `{{ ... }}`, `{% ... %}`, `*|...|*`, `[[...]]`, `%%...%%`, `{...}` — depending on ESP syntax\n- [ ] **All merge tags well-formed** — balanced brackets, closed blocks\n- [ ] **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.\n- [ ] **\"Hi ,\" or \"Hello ,\" anti-pattern** — substring search for bare punctuation after a greeting. This is the #1 embarrassing merge-tag failure.\n- [ ] **Dynamic content blocks closed** — open `{% if %}` must have `{% endif %}`; same for loops\n- [ ] **No raw tokens in subject line** — e.g., subject reading literally \"Hi {{ first_name }}\"\n\n#### 2.3 Links\nExtract all `<a href=\"...\">` from the HTML. For each:\n- [ ] **URL is absolute** — no `href=\"/path\"` without domain; no `href=\"#\"` unless intentional anchor\n- [ ] **URL resolves** — HEAD-check each unique URL (cap at 30 requests, dedupe). Flag non-200s.\n- [ ] **No `localhost` / `127.0.0.1` / `.local`** — dev leak\n- [ ] **No staging/dev domains** — flag `*.staging.*`, `*.dev.*`, `stage.`, `preprod.`\n- [ ] **UTM parameters present on all outbound links** — warn on links missing `utm_source`/`utm_medium`/`utm_campaign`\n- [ ] **UTM consistency** — all links should share `utm_campaign`; flag divergence\n- [ ] **Link count** — report total. Warn if >20 (clutter). Warn if <1 (no CTA).\n- [ ] **CTA destination consistency** — multiple buttons labeled \"Shop now\" should generally point to the same URL\n\n#### 2.4 Images\n- [ ] **All `<img>` have `alt` text** — non-empty, descriptive\n- [ ] **No pure-image email** — flag if text-to-image ratio is <40% text. Hero-image-only emails trigger spam filters.\n- [ ] **Image URLs are absolute + HTTPS**\n- [ ] **Image URLs aren't hotlinked from sketchy hosts** — flag if images are on `imgur`, `cloudinary demo`, etc. instead of a brand CDN\n\n#### 2.5 Compliance\n- [ ] **Unsubscribe link present** — regex for `unsubscribe`, `preferences`, `opt out`\n- [ ] **Unsubscribe link is visible** — not `display:none` or font-size:1px\n- [ ] **Physical postal address in footer** — CAN-SPAM requirement. Regex for a street/city pattern or flag if missing.\n- [ ] **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)\n- [ ] **Preference center link** — nice-to-have; warn if only unsubscribe exists\n\n#### 2.6 Spam triggers\nScan subject + preheader + body for classic triggers and report offenders:\n- **Word-level:** free, winner, congratulations, guaranteed, risk-free, act now, click here, limited time, no obligation, viagra (lol), make money, earn extra, $$$\n- **Punctuation:** more than one `!` in subject, `!!!`, `???`\n- **Casing:** ALL CAPS WORDS longer than 3 chars in subject\n- **Symbols:** `$$$`, `★`, excessive emoji (>3)\n- **HTML:** hidden text (color matches background), tiny font, embedded JavaScript, background images on `<body>`\n\nOutput each offender with context:\n```\n🔴 Subject contains \"!!!\"\n🟡 Body has 4 instances of ALL CAPS words: \"LIMITED\", \"TODAY\", \"ACT NOW\", \"DON'T\"\n```\n\n#### 2.7 Rendering & size\n- [ ] **HTML size < 102 KB** — Gmail clips at 102 KB. Report actual size.\n- [ ] **Single root `<table>` layout** (desktop email standard) OR uses MJML — flag flex/grid as a deliverability risk\n- [ ] **Inline CSS** — style attributes vs `<style>` blocks. Many clients strip `<head>` styles.\n- [ ] **Mobile viewport meta** — `<meta name=\"viewport\" content=\"width=device-width\">`\n- [ ] **Font stack has fallbacks** — no `font-family: \"Custom Font\";` without a web-safe fallback\n- [ ] **No `<script>`, no `<form>`, no external fonts loaded via JS** — all stripped by most clients\n\n#### 2.8 Send context (MCP mode only)\n- [ ] **Target segment is populated** — recipient count > 0\n- [ ] **Audience isn't the entire list for a promotional send** — flag \"All Subscribers\" when there's a segmented alternative\n- [ ] **Scheduled time isn't 3am recipient-local** — flag weird send hours\n- [ ] **Not a duplicate send** — check last 30 days for a campaign with the same subject going to the same segment\n\n### 3. Report\n\n```\nPre-send QA: <campaign name | \"pasted email\">\nRecipient count: <N>    Send ETA: <time or \"not scheduled\">\n\nOverall: <READY TO SEND | FIX BEFORE SEND | CRITICAL ISSUES>\n\n🔴 Critical (will break or embarrass)\n  — <specific issue with offending text / URL / line>\n  — ...\n\n🟡 Warnings (fix if you have time)\n  — ...\n\n🟢 Passed\n  — <count> checks passed\n\n──\nRecommended fix order:\n  1. ...\n  2. ...\n\nMerge-tag fallback cheatsheet for <ESP>:\n  [ESP-specific syntax examples]\n```\n\n### 4. Finding (optional)\n\nIf critical issues are found and the campaign is scheduled to send soon, create a finding:\n\n```json\n{\n  \"title\": \"Campaign '<name>' has critical QA issues — blocked send\",\n  \"body\": \"<enumerated issues>\",\n  \"action_type\": \"pre_send_fix\",\n  \"priority\": \"high\"\n}\n```\n\n### 5. Do not auto-fix\n\nNever modify the campaign automatically. Always present the diff and let the user approve each fix.\n\n## Notes\n\n- ESP-specific merge tag syntax:\n  - **Klaviyo:** `{{ first_name|default:\"there\" }}`\n  - **Mailchimp:** `*|FNAME|*` with `*|IFNOT:FNAME|*there*|ELSE:*|FNAME|**|END:IF|*`\n  - **Rule / Get a Newsletter:** `[Firstname]` with platform-specific fallback syntax\n- Spam-trigger lists are heuristics; modern spam filters use ML + reputation. Treat warnings as \"raise eyebrow, check reputation\" not \"instant spam\".","tags":["pre","send","claude","code","marketing","skills","cognyai","agent-skills","ai-agents","claude-code","claude-skills","cluade-mcp"],"capabilities":["skill","source-cognyai","skill-pre-send-qa","topic-agent-skills","topic-ai-agents","topic-claude-code","topic-claude-skills","topic-cluade-mcp","topic-cursor","topic-geo","topic-growth-hacking","topic-llm","topic-marketing","topic-mcp","topic-seo"],"categories":["claude-code-marketing-skills"],"synonyms":[],"warnings":[],"endpointUrl":"https://skills.sh/cognyai/claude-code-marketing-skills/pre-send-qa","protocol":"skill","transport":"skills-sh","auth":{"type":"none","details":{"cli":"npx skills add cognyai/claude-code-marketing-skills","source_repo":"https://github.com/cognyai/claude-code-marketing-skills","install_from":"skills.sh"}},"qualityScore":"0.474","qualityRationale":"deterministic score 0.47 from registry signals: · indexed on github topic:agent-skills · 48 github stars · SKILL.md body (8,108 chars)","verified":false,"liveness":"unknown","lastLivenessCheck":null,"agentReviews":{"count":0,"score_avg":null,"cost_usd_avg":null,"success_rate":null,"latency_p50_ms":null,"narrative_summary":null,"summary_updated_at":null},"enrichmentModel":"deterministic:skill-github:v1","enrichmentVersion":1,"enrichedAt":"2026-05-18T18:58:07.400Z","embedding":null,"createdAt":"2026-04-22T18:59:08.381Z","updatedAt":"2026-05-18T18:58:07.400Z","lastSeenAt":"2026-05-18T18:58:07.400Z","tsv":"'/path':519 '/pre-send-qa':89,103 '1':112,474,595 '100':364 '102':848,853 '127.0.0.1':546 '1px':695 '2':315 '2.1':337 '2.2':407 '2.3':505 '2.4':613 '2.5':673 '2.6':752 '2.7':843 '2.8':226,300 '20':591 '200s':543 '3':801,808 '30':537 '4':831 '40':636 '404s':265 '55':347 '85':363 'absolut':516,649 'accept':294 'access':66 'act':775,839 'actual':856 'address':391,698 'allow':721 'also':218 'alt':617 'anchor':526 'anti':461 'anti-pattern':460 'aren':653 'ask':284 'associ':221 'attribut':876 'b':282 'back':258 'background':814,819 'balanc':426 'bare':466 'block':429,482 'bodi':210,212,292,412,758,829 'bracket':427 'brand':377,671 'broken':15,53 'bulk':395,738 'button':602 'campaign':12,72,93,118,148,149,154,155,160,161,236,574,582 'campaign-id':71 'can-spam':57,701 'cap':535,797,835 'case':795 'catch':30 'categori':322 'cdn':672 'center':741 'char':348,365,802 'check':46,87,126,531 'checklist':318 'classic':760 'click':731,777 'clip':851 'close':428,483 'cloudinari':665 'clutter':592 'cogni':68,130 'color':812 'common':255 'complianc':19,674 'configur':728 'congratul':770 'connect':125 'consist':576,600 'contain':828 'content':336,481 'context':86,302,826 'count':229,310,586 'css':874 'cta':597,598 'dedup':539 'default':438 'deliver':392,871 'demo':666 'depend':416 'descript':622 'desktop':861 'destin':599 'detect':121 'dev':548,555 'display':689 'diverg':584 'domain':521,552 'draft':95,171,173,247 'duplic':358 'dynam':480 'e.g':435,498 'earn':787 'els':444 'email':11,49,107,115,205,383,627,642,862 'embarrass':32,475 'embed':817 'emoji':807 'empti':344,355,621 'end':446 'endif':488 'esp':102,123,138,198,418,720 'estat':370 'etc':667 'everi':197,321 'excess':806 'exist':751 'extra':788 'extract':199,507 'fail':330,456 'failur':479 'fall':257 'fallback':52,434,454 'fetch':139,219 'filter':645 'find':279 'first':248,436,503 'flag':387,449,540,553,583,628,659,711,718,867 'flex/grid':868 'flight':7,45 'fname':442,445 'font':693,816 'font-siz':692 'footer':61,700 'forens':186 'form':425 'free':768,774 'full':42 'gdpr':60 'general':607 'get':97,147,153,159,163,170,180,188,191,230,246,261 'gmail':850 'gmail.com':388 'gmail/yahoo':736 'greet':470 'guarante':771 'hasn':175 'head':530 'head-check':529 'header':338,717 'hello':459 'hero':639 'hero-image-on':638 'hi':457,502 'hidden':810 'hit':24 'host':658 'hotlink':655 'href':518,523 'html':77,209,293,511,809,846 'html/mjml/text':108 'https':650 'id':73,119,143,243 'ifnot':441 'imag':614,626,633,640,646,651,661,820 'imgur':664 'includ':332 'info':381 'inlin':873 'instanc':832 'instead':668 'intent':525 'item':195,325 'javascript':818 'kb':849,854 'killer':393 'klaviyo':146,440 'label':603 'layout':860 'leak':549 'length':346,362 'level':767 'limit':65,779,837 'line':287,297,341,497 'link':16,54,506,564,567,578,585,676,685,742 'list':39,276,715 'list-unsubscrib':714 'list/audience':222 'liter':501 'load':113 'local':547 'localhost':545 'lol':784 'long':372 'longer':799 'loop':491 'mailchimp':151,448 'make':785 'mark':326 'match':813 'mcp':69,81,120,129,132 'medium':572 'merg':13,50,408,421,431,477 'merge-tag':476 'miss':568,713 'mjml':866 'mobil':20,62,349 'mode':74,78,116,281,307 'money':786 'monitor':400 'multi':296 'multi-lin':295 'multipl':601 'must':486 'name':203,375,437,504 'name/email':290 'namespac':128 'newslett':99,165,182,232 'nice':744 'nice-to-hav':743 'non':542,620 'non-200s':541 'non-empti':619 'none':690 'norepli':379,406 'object':150,156,162,237 'oblig':782 'offend':335,764,824 'one':730,792 'one-click':729 'open':484 'opt':682 'outbound':563 'outlook.com':389 'output':822 'paramet':559 'pass':241,327 'past':76,105,283,298,306 'paste-html':75 'pattern':462,709 'person':410 'physic':696 'point':608 'post':184 'post-send':183 'postal':697 'pre':2,6,27,44,167,250 'pre-flight':5,43 'pre-send':26,166,249 'pre-send-qa':1 'prefer':681,740 'prehead':201,288,352,361,414,757 'preprod':557 'present':342,353,560,677 'prompt':110 'punctuat':467,789 'pure':625 'pure-imag':624 'qa':4,8,29,90,252,270 'ratio':634 'raw':493 'read':500 'real':369 'recipi':228,309 'refer':144 'regex':678,705 'render':21,63,844 'repli':207,397 'reply-to':206,396 'report':192,587,763,855 'request':538 'requir':67,704,734 'resolv':528 'right':136 'risk':773,872 'risk-fre':772 'root':859 'rout':339 'rule':157 'run':40,316 'scan':411,755 'schedul':215,272,277,314 'search':464 'section':225,299 'segment':312 'segment/list':214 'send':3,25,28,85,168,185,216,251,273,301,385 'send-context':84 'sender':739 'sent':178,189,194,262 'set':722 'share':580 'ship':36 'shop':604 'short':367 'singl':235,858 'size':64,223,694,845,847,857 'sketchi':657 'skill' 'skill-pre-send-qa' 'skip':304 'sourc':570 'source-cognyai' 'spam':17,55,59,644,703,753 'specif':92,334 'stage':554,556 'staging/dev':551 'standard':863 'step':111 'street/city':708 'stuff':33 'style':875 'subdomain':386 'subject':200,286,340,345,360,413,496,499,756,794,804,827 'substr':463 'symbol':805 'syntax':419 'tag':14,51,409,422,432,478 'target':213 'text':211,618,631,637,811 'text-to-imag':630 'threshold':351 'time':217,780 'tini':815 'today':838 'token':451,494 'tool':137,140,152,158,169,187,190,245,260,275 'topic-agent-skills' 'topic-ai-agents' 'topic-claude-code' 'topic-claude-skills' 'topic-cluade-mcp' 'topic-cursor' 'topic-geo' 'topic-growth-hacking' 'topic-llm' 'topic-marketing' 'topic-mcp' 'topic-seo' 'total':588 'tri':244 'trigger':18,56,643,754,761 'truncat':350,373 'uniqu':533 'unless':524 'unsubscrib':675,680,684,716,732,750 'url':514,527,534,612,647,652 'usag':88 'use':134,256,274,384,865 'user':240 'utm':558,569,571,573,575,581 'viagra':783 'visibl':687 'vs':877 'walk':319 'want':268 'warn':328,401,565,589,593,747 'wast':368 'well':424 'well-form':423 'winner':769 'without':80,83,452,520 'word':766,798,836 'word-level':765 'work':79 'yet':179","prices":[{"id":"d2905197-39ac-446a-a78f-647e7544b3d4","listingId":"39519b9d-3853-4ad0-8959-2d312d1dfce5","amountUsd":"0","unit":"free","nativeCurrency":null,"nativeAmount":null,"chain":null,"payTo":null,"paymentMethod":"skill-free","isPrimary":true,"details":{"org":"cognyai","category":"claude-code-marketing-skills","install_from":"skills.sh"},"createdAt":"2026-04-22T18:59:08.381Z"}],"sources":[{"listingId":"39519b9d-3853-4ad0-8959-2d312d1dfce5","source":"github","sourceId":"cognyai/claude-code-marketing-skills/pre-send-qa","sourceUrl":"https://github.com/cognyai/claude-code-marketing-skills/tree/main/skills/pre-send-qa","isPrimary":false,"firstSeenAt":"2026-04-22T18:59:08.381Z","lastSeenAt":"2026-05-18T18:58:07.400Z"}],"details":{"listingId":"39519b9d-3853-4ad0-8959-2d312d1dfce5","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"cognyai","slug":"pre-send-qa","github":{"repo":"cognyai/claude-code-marketing-skills","stars":48,"topics":["agent-skills","ai-agents","claude-code","claude-skills","cluade-mcp","cursor","geo","growth-hacking","llm","marketing","mcp","seo","vibe","windsurf"],"license":null,"html_url":"https://github.com/cognyai/claude-code-marketing-skills","pushed_at":"2026-05-07T20:57:53Z","description":"Marketing skills for Claude Code — SEO audits and implementation, ad analysis, ad optimization. Free skills need no account. $9/mo for live Search Console, Bing & LinkedIn data.","skill_md_sha":"365e5483580db5c3c7f1d34f9a5f53a0b831b0ee","skill_md_path":"skills/pre-send-qa/SKILL.md","default_branch":"main","skill_tree_url":"https://github.com/cognyai/claude-code-marketing-skills/tree/main/skills/pre-send-qa"},"layout":"multi","source":"github","category":"claude-code-marketing-skills","frontmatter":{"name":"pre-send-qa","description":"Pre-flight QA for an email campaign — merge tags, broken links, spam triggers, compliance, mobile rendering — before you hit send"},"skills_sh_url":"https://skills.sh/cognyai/claude-code-marketing-skills/pre-send-qa"},"updatedAt":"2026-05-18T18:58:07.400Z"}}