Skip to content
Back to skills

Email Compliance

ASecurity

Use when a product sends email or collects addresses for newsletters, product updates, promotions, or lifecycle campaigns, and you need to verify sender identity, transactional versus marketing separation, and that unsubscribe actually suppresses future sends. It traces the unsubscribe from link to stored state to the send path. Do not use it to stop at the presence of a link, to send real email to real people, or to state legal sufficiency.

  • 2 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 1, 2026
ai-agentsgonodeapibackendsecuritydocumentation

Works with

  • cli
  • api

Security analysis

A100/100

Pro scans all 11 files and shows the line behind each finding

Scanned October 1, 2026

npx -y skills add moh-obaida/ReadyVibe-Skills --skill email-compliance --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Email Compliance?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Email Compliance
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/moh-obaida-email-compliance/badge)](https://www.skillsdirectory.com/skills/moh-obaida-email-compliance)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: email-compliance
description: "Use when a product sends email or collects addresses for newsletters, product updates, promotions, or lifecycle campaigns, and you need to verify sender identity, transactional versus marketing separation, and that unsubscribe actually suppresses future sends. It traces the unsubscribe from link to stored state to the send path. Do not use it to stop at the presence of a link, to send real email to real people, or to state legal sufficiency."
license: Apache-2.0
metadata:
  kind: specialist
  helpers: "observe-runtime,check-links"
  launch-checks: "36,37"
  compliance-domains: "4"
  references: "official-sources"
  companions: "design-system-reconnaissance"
---

# email-compliance

"Unsubscribe link exists" is not "unsubscribe works", and neither is "future marketing is suppressed". The failure that hurts people is the third one: they unsubscribed and kept getting mail.

## Activate when

- The product has a newsletter or waitlist form, an email provider (Resend, SendGrid, Postmark, Mailchimp, Loops, SES…), email templates, send routes, or scheduled campaigns.
- Not when no email is sent and no address is collected for messaging (record "not applicable" with the observed reason and the recheck trigger). Not to design campaigns.

## Working alone

This skill is self-contained. Its **companions** (declared in its metadata) are skills whose method it may need to do its own promised work. Use of a companion can be conditional: declaring one does not mean running it. When a companion's lane applies, use the skill if it is installed; if not, follow its short entry in [references/companion-methods.md](references/companion-methods.md) and say in your report which lanes ran inline at reduced depth. Never skip an applicable lane silently. Skills mentioned here only for escalation, referral, documentation, or optional deeper follow-up are not dependencies: report the hand-off and finish honestly.

Companions: `design-system-reconnaissance`.

## Inspect

1. **Classify each email** the product can send: **transactional** (receipt, password reset, security notice, requested confirmation) vs **marketing/promotional** (newsletter, announcements, upsell, re-engagement) vs **mixed** (transactional with promo content, which is where problems begin). Marketing content in a transactional email changes its category.
2. **Collection.** Signup forms and their wording: what the person is told they will receive; pre-ticked boxes; whether address collection for one purpose (account) is reused for another (marketing) without a separate choice; double opt-in if promised; where the subscription record is stored (list/provider or own DB) with timestamp and source if available.
3. **Sender identity.** From name/address, reply-to that receives replies, physical/business identification in the footer where relevant, no misleading subject or headers, custom-domain authentication config (SPF/DKIM/DMARC) if visible in DNS docs or provider config (report as SOURCE-INDICATED).
4. **Unsubscribe, end to end:**
   - **Link exists** in every marketing template, is not `#` or a placeholder, and points at a real route.
   - **Route works** (on staging with a test address: `node scripts/observe-runtime.mjs --url <staging-url> --steps unsubscribe.json` with `goto`, `click`, and `snapshot` steps, or by hand; paths relative to this skill's folder): resolves without login, one step or one confirm, no dark patterns, clear confirmation.
   - **`List-Unsubscribe` / `List-Unsubscribe-Post` headers** present on marketing sends if the provider sends them (provider config or code).
   - **State changes:** find the code that handles it. Does it set a suppression flag / delete the subscription / call the provider's suppression API? Is it keyed by the *address* (case-normalized) as well as by the user, so the same address on another list or account is covered?
   - **Send path honors it:** locate every place marketing is sent (cron, campaign job, provider automation, admin "send to all"). Does each query exclude suppressed addresses **before** sending? A single unfiltered path is a bug.
   - **Provider vs app state:** if the provider holds the list, does the app sync unsubscribes back? Do bounces and complaints suppress?
5. **Preference controls** where implemented: do categories save, and are they enforced in the send path?
6. **Links in emails**: unsubscribe, view-in-browser, and CTAs resolve; no localhost or staging hosts (`node scripts/check-links.mjs --url <template-preview-url>`).
7. **Do not send** to real addresses. Use provider sandboxes, a local mail catcher, or preview rendering. On a staging backend use a `@example.test` address.

## Evidence that counts

Label each claim OBSERVED, SOURCE-INDICATED, DECLARED, INFERRED, UNKNOWN, or REVIEW REQUIRED. UNKNOWN is never a pass and never a failure.

- OBSERVED needs the unsubscribe run on staging with a test address **and** a check that the address is excluded by the send-selection logic (a test send to the sandbox, or a query result).
- Handler sets a flag: SOURCE-INDICATED suppression. Add "send path filter verified" to upgrade it.
- Provider-side suppression you cannot inspect is UNKNOWN. Say what the owner should confirm in the provider dashboard.
- **Legal specifics: never from memory.** When a rule, deadline, threshold, or required wording matters, read the current text or guidance at an official source while you run (start from [references/official-sources.md](references/official-sources.md)), cite the source and access date, and treat applicability to this business as REVIEW REQUIRED. If you cannot look it up, the answer is UNKNOWN.

## May change

**Design first.** Before creating or changing anything visible, inspect the project's existing design system (`design-system-reconnaissance`) and build from its tokens and components, by the component ladder: reuse, compose, extend, and only then create a matching component. Never impose a ReadyVibe look on the user's site.

- Fix a placeholder/dead unsubscribe link; wire an existing unsubscribe route to the real suppression mechanism; add the suppression filter to send queries; add `List-Unsubscribe` headers through the existing provider integration; fix localhost/staging URLs in templates; add an honest signup line near the form describing what will be sent ("Product updates, about monthly").
- Separate a marketing block out of a transactional email **only** with the owner's decision.
- Never send email, import lists, change provider accounts, or fabricate a sender address, business address, or consent record.

## Must not claim

"CAN-SPAM/GDPR/CASL compliant", "opted-in", "consent recorded" without evidence, or "unsubscribe works" when only the link was checked. Do not state jurisdiction-specific requirements (address in footer, opt-in vs opt-out, time limits) from memory.

## Verify

On staging with a test address: subscribe → confirm (if double opt-in) → trigger a marketing send to the sandbox (received) → unsubscribe → trigger the send again (**not** received) → resubscribe path (if any) works and is deliberate. Check that the unsubscribe survives a redeploy (stored, not in memory) and applies across lists as intended.

## Escalate

Consent basis, sender identification content, and regional requirements: REVIEW REQUIRED. A marketing path that ignores suppression and could email real users: HIGH, and warn the owner not to send until fixed. Purchased or imported lists: stop and ask.

## No change is valid when

No marketing email exists (no provider, no newsletter form, no send route), or only transactional messages are sent and templates contain no promotional content. Say so and name the recheck trigger (adding a newsletter form or email vendor).

Files in this skill

  • SKILL.md7.6 KB
  • references/companion-methods.md2 KB
  • references/official-sources.md6.5 KB
  • scripts/check-links.mjs14.4 KB
  • scripts/lib/browser.mjs3.3 KB
  • scripts/lib/canary.mjs1.5 KB
  • scripts/lib/html.mjs5.8 KB
  • scripts/lib/pages.mjs11.3 KB
  • scripts/lib/report.mjs3 KB
  • scripts/lib/trackers.mjs5.9 KB
  • scripts/observe-runtime.mjs25.1 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…