← back to Majilite Onboard
Draft Constant Contact campaign for Majilite Metallic Specialties I (email HTML + spec)
8fafdafb27ff3d51b425cc3f8c4d545932b51375 · 2026-08-10 11:54:52 -0700 · Steve Abrams
Files touched
A campaign/campaign.jsonA campaign/majilite_email.htmlA scripts/cc_probe.js
Diff
commit 8fafdafb27ff3d51b425cc3f8c4d545932b51375
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Mon Aug 10 11:54:52 2026 -0700
Draft Constant Contact campaign for Majilite Metallic Specialties I (email HTML + spec)
---
campaign/campaign.json | 19 ++++++++++++
campaign/majilite_email.html | 71 ++++++++++++++++++++++++++++++++++++++++++++
scripts/cc_probe.js | 29 ++++++++++++++++++
3 files changed, 119 insertions(+)
diff --git a/campaign/campaign.json b/campaign/campaign.json
new file mode 100644
index 0000000..ddbb214
--- /dev/null
+++ b/campaign/campaign.json
@@ -0,0 +1,19 @@
+{
+ "platform": "Constant Contact",
+ "campaign_name": "Majilite Metallic Specialties I — New Collection",
+ "subject": "New: Majilite Metallic Specialties I — 160 luxe metallic finishes",
+ "preheader": "Breathable, durable Nytek® faux leather for walls & seating. Order swatches.",
+ "from_name": "Designer Wallcoverings & Fabrics",
+ "from_email": "TBD — must be a CONFIRMED sender in the CC account (e.g. info@designerwallcoverings.com)",
+ "reply_to_email": "info@designerwallcoverings.com",
+ "html_file": "majilite_email.html",
+ "cta_url": "https://www.designerwallcoverings.com/collections/majilite-metallic-specialties-i",
+ "target_list": "TBD — Steve picks the CC contact list (e.g. Trade / All Customers)",
+ "status_intended": "DRAFT (create only; scheduling/sending is Steve-gated send-to-list)",
+ "compliance": {
+ "can_spam_footer": "physical address + unsubscribe via CC merge tokens [[physical_address]] / [[UNSUBSCRIBE]]",
+ "subject_screen": "no ALL-CAPS, no RE:/FW:, no $ in subject (price kept in body) — passes hard+soft screen",
+ "note": "CC auto-injects the account's registered physical address + unsubscribe at send"
+ },
+ "how_to_create": "Via Connie (cc-agent :9820) which holds the live rotating CC token, or the marketing-command-center CC module. Local master CC_REFRESH_TOKEN is expired by design (CC rotates it; Connie holds the live one)."
+}
diff --git a/campaign/majilite_email.html b/campaign/majilite_email.html
new file mode 100644
index 0000000..0206d21
--- /dev/null
+++ b/campaign/majilite_email.html
@@ -0,0 +1,71 @@
+<!-- Constant Contact campaign — Majilite Metallic Specialties I (email-safe tables + inline styles) -->
+<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="background:#0d0f12;margin:0;padding:0">
+<tr><td align="center" style="padding:0">
+ <table role="presentation" width="600" cellpadding="0" cellspacing="0" style="max-width:600px;width:100%;background:#14171c;font-family:Helvetica,Arial,sans-serif;color:#e9ecf1">
+
+ <!-- wordmark -->
+ <tr><td align="center" style="padding:22px 24px 8px">
+ <div style="font-size:13px;letter-spacing:.28em;color:#b7bcc2;text-transform:uppercase">Designer Wallcoverings & Fabrics</div>
+ </td></tr>
+
+ <!-- hero -->
+ <tr><td style="padding:0">
+ <img src="https://cdn.shopify.com/s/files/1/0015/4117/7456/files/DWMJ-600158.png" width="600" alt="Majilite Metallic Specialties I" style="display:block;width:100%;height:auto;border:0"/>
+ </td></tr>
+
+ <!-- headline -->
+ <tr><td align="center" style="padding:26px 30px 6px">
+ <div style="font-size:12px;letter-spacing:.22em;color:#5fd08a;text-transform:uppercase;margin-bottom:10px">New Collection · The Nytek® Collection</div>
+ <h1 style="margin:0;font-size:30px;line-height:1.15;font-weight:400;letter-spacing:.01em;color:#ffffff;font-style:italic">Majilite Metallic Specialties I</h1>
+ </td></tr>
+
+ <!-- body -->
+ <tr><td align="center" style="padding:14px 40px 6px">
+ <p style="margin:0;font-size:15px;line-height:1.6;color:#c9ced6">
+ <strong>160 luxurious metallic & neutral finishes</strong> in Majilite's engineered
+ <em>Nytek®</em> faux leather — breathable, remarkably durable (300,000+ double-rubs), and
+ <strong>PFAS-, PVC- & plasticizer-free</strong>. Ideal for wall coverings, seating,
+ flat surfaces & displays.
+ </p>
+ </td></tr>
+
+ <!-- swatch trio -->
+ <tr><td style="padding:22px 30px 6px">
+ <table role="presentation" width="100%" cellpadding="0" cellspacing="0"><tr>
+ <td width="33%" style="padding:4px"><img src="https://cdn.shopify.com/s/files/1/0015/4117/7456/files/DWMJ-600007.png" width="100%" alt="Reflection Aluminum" style="display:block;width:100%;height:auto;border-radius:6px;border:0"/></td>
+ <td width="33%" style="padding:4px"><img src="https://cdn.shopify.com/s/files/1/0015/4117/7456/files/DWMJ-600120.png" width="100%" alt="Rose Gold" style="display:block;width:100%;height:auto;border-radius:6px;border:0"/></td>
+ <td width="33%" style="padding:4px"><img src="https://cdn.shopify.com/s/files/1/0015/4117/7456/files/DWMJ-600160.png" width="100%" alt="Hammered Metal Midnight" style="display:block;width:100%;height:auto;border-radius:6px;border:0"/></td>
+ </tr></table>
+ </td></tr>
+
+ <!-- CTA -->
+ <tr><td align="center" style="padding:22px 30px 8px">
+ <a href="https://www.designerwallcoverings.com/collections/majilite-metallic-specialties-i"
+ style="display:inline-block;background:#5b6cff;color:#ffffff;text-decoration:none;font-size:15px;font-weight:600;letter-spacing:.02em;padding:14px 34px;border-radius:8px">
+ Shop the Collection →
+ </a>
+ <div style="margin-top:12px;font-size:13px;color:#8a929e">Order any swatch as a <strong style="color:#c9ced6">$4.25 sample</strong>.</div>
+ </td></tr>
+
+ <!-- reassurance line -->
+ <tr><td align="center" style="padding:16px 40px 8px">
+ <div style="font-size:12px;line-height:1.6;color:#8a929e;border-top:1px solid #232830;padding-top:18px">
+ 54" wide · sold by the yard · inherent flame resistance (NFPA-260 Class 1) · made in Massachusetts.<br>
+ Now stocked at Designer Wallcoverings & Fabrics.
+ </div>
+ </td></tr>
+
+ <!-- CAN-SPAM footer: CC injects the account's registered physical address + a working
+ unsubscribe link at send time. Tokens below satisfy the pre-flight gate. -->
+ <tr><td align="center" style="padding:0 40px 26px">
+ <div style="font-size:11px;line-height:1.6;color:#6b7079">
+ Designer Wallcoverings & Fabrics · (888) 373-4564 · DesignerWallcoverings.com<br>
+ [[physical_address]]<br>
+ You received this because you're a Designer Wallcoverings customer or trade contact.
+ <a href="[[UNSUBSCRIBE]]" style="color:#8a929e">Unsubscribe</a> or update your preferences.
+ </div>
+ </td></tr>
+
+ </table>
+</td></tr>
+</table>
diff --git a/scripts/cc_probe.js b/scripts/cc_probe.js
new file mode 100644
index 0000000..7d42a6f
--- /dev/null
+++ b/scripts/cc_probe.js
@@ -0,0 +1,29 @@
+// Constant Contact v3 — verify creds, refresh token (save rotated), read senders/address/lists.
+const fs=require('fs'),path=require('path');
+const ENV=path.join(process.env.HOME,'Projects/secrets-manager/.env');
+const env=fs.readFileSync(ENV,'utf8');
+const g=k=>((env.match(new RegExp('^'+k+'=(.+)$','m'))||[])[1]||'').replace(/["']/g,'').trim();
+const CID=g('CC_CLIENT_ID'), CSEC=g('CC_CLIENT_SECRET'), RTOK=g('CC_REFRESH_TOKEN');
+(async()=>{
+ if(!CID||!CSEC||!RTOK){console.log('missing CC creds', {CID:!!CID,CSEC:!!CSEC,RTOK:!!RTOK});return;}
+ const basic=Buffer.from(CID+':'+CSEC).toString('base64');
+ const body=new URLSearchParams({grant_type:'refresh_token',refresh_token:RTOK});
+ const tr=await fetch('https://authz.constantcontact.com/oauth2/default/v1/token',{method:'POST',headers:{'Authorization':'Basic '+basic,'Content-Type':'application/x-www-form-urlencoded'},body});
+ const tj=await tr.json();
+ if(!tj.access_token){console.log('TOKEN FAIL', JSON.stringify(tj).slice(0,200));return;}
+ const AT=tj.access_token;
+ // persist rotated refresh token immediately
+ if(tj.refresh_token && tj.refresh_token!==RTOK){
+ let e=fs.readFileSync(ENV,'utf8'); e=e.replace(/^CC_REFRESH_TOKEN=.*$/m,'CC_REFRESH_TOKEN='+tj.refresh_token); fs.writeFileSync(ENV,e);
+ console.log('rotated CC_REFRESH_TOKEN saved (…'+tj.refresh_token.slice(-6)+')');
+ }
+ fs.writeFileSync('/tmp/majilite/cc_at.txt', AT);
+ const api=(p)=>fetch('https://api.cc.email/v3'+p,{headers:{'Authorization':'Bearer '+AT,'Accept':'application/json'}}).then(r=>r.json());
+ const sum=await api('/account/summary?extra_fields=physical_address');
+ console.log('ACCOUNT:', sum.organization_name||sum.email||'?', '| addr:', JSON.stringify(sum.physical_address||{}).slice(0,120));
+ const emails=await api('/account/emails');
+ const verified=(Array.isArray(emails)?emails:[]).filter(e=>e.confirm_status==='CONFIRMED');
+ console.log('VERIFIED SENDERS:', verified.map(e=>e.email_address).join(', ')||JSON.stringify(emails).slice(0,150));
+ const lists=await api('/contact_lists?include_count=true');
+ console.log('LISTS:', (lists.lists||[]).map(l=>l.name+' ('+(l.membership_count||0)+') ['+l.list_id+']').join(' | ').slice(0,400));
+})();
← 8889c9b Publish all 160 Majilite + collection to ALL 13 sales channe
·
back to Majilite Onboard
·
chore: session-close quality gate (lint ✓ 0-fix, refactor ✓ ee8bbbf →