[object Object]

← back to Wild Wallcoverings

Whimsical launch fired: IG reel + Slot 9 live (verified permalinks), 8-post grid queue + launchd draft, TikTok/Pinterest manual packages

75a502d3b7b526e55d408e804ff23ef0deadeb0b · 2026-07-23 08:08:33 -0700 · Steve Abrams

Files touched

Diff

commit 75a502d3b7b526e55d408e804ff23ef0deadeb0b
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Thu Jul 23 08:08:33 2026 -0700

    Whimsical launch fired: IG reel + Slot 9 live (verified permalinks), 8-post grid queue + launchd draft, TikTok/Pinterest manual packages
---
 marketing/README.md                       |  27 +++++++++-
 marketing/feed/whimsical-slot1.jpg        | Bin 0 -> 380520 bytes
 marketing/feed/whimsical-slot2.jpg        | Bin 0 -> 76972 bytes
 marketing/feed/whimsical-slot3.jpg        | Bin 0 -> 232679 bytes
 marketing/feed/whimsical-slot4.jpg        | Bin 0 -> 442310 bytes
 marketing/feed/whimsical-slot5.jpg        | Bin 0 -> 87560 bytes
 marketing/feed/whimsical-slot6.jpg        | Bin 0 -> 308499 bytes
 marketing/feed/whimsical-slot7.jpg        | Bin 0 -> 132271 bytes
 marketing/feed/whimsical-slot8.jpg        | Bin 0 -> 261167 bytes
 marketing/feed/whimsical-slot9.jpg        | Bin 0 -> 120778 bytes
 scripts/com.steve.whimsical-ig-grid.plist |  23 ++++++++
 scripts/ig-grid-post-next.mjs             |  86 ++++++++++++++++++++++++++++++
 scripts/ig-grid-queue.json                |  71 ++++++++++++++++++++++++
 13 files changed, 206 insertions(+), 1 deletion(-)

diff --git a/marketing/README.md b/marketing/README.md
index 388c05a..91beb74 100644
--- a/marketing/README.md
+++ b/marketing/README.md
@@ -1,6 +1,31 @@
 # Whimsical Wallcoverings — Launch Marketing Package
 
-Built 2026-07-23. **Everything here is DRAFT-ONLY. Nothing is posted, scheduled, or pinned. Steve fires each channel explicitly.** Total build cost: **$0 (local)** — ImageMagick + ffmpeg + local PG/Admin-API reads; no paid API calls.
+Built 2026-07-23. Total build cost: **$0 (local)** — ImageMagick + ffmpeg + local PG/Admin-API reads; no paid API calls.
+
+## LAUNCH STATUS (fired 2026-07-23 on Steve's explicit go)
+
+| Channel | Status |
+|---|---|
+| IG reel (Caption A) | **LIVE** — media 17973907902089874, https://www.instagram.com/reel/DbI8WntDQns/ (first comment = collection URL, comment id 18120044812873062) |
+| IG grid Slot 9 (Regatta Royale) | **LIVE** — media 18104822393278517, https://www.instagram.com/p/DbI8ecNEvn-/ (first comment = product URL) |
+| IG grid Slots 8→1 | **QUEUED** — one/day via `scripts/ig-grid-post-next.mjs` + `scripts/ig-grid-queue.json`; launchd plist DRAFT at `scripts/com.steve.whimsical-ig-grid.plist` (bootstrap paste below — scheduled-job gate) |
+| TikTok | **Steve's hands** (~3 min) — package on `~/Desktop/tiktok-whimsical-launch/` (MP4 + Caption C one-pager). API path is private-only pre-App-Review. |
+| Pinterest | **Steve's hands** (~10 min) — no DW Pinterest API token exists; board + 10 pins per `pinterest-plan.md`, images in `pins/` |
+
+**Token note:** Norma's own IG token (…fgZD) EXPIRED 2026-07-20 (Graph error 190, confirmed live 2026-07-23) — posting went out via the healthy never-expiring `META_ACCESS_TOKEN` (…ZDZD, app "DW Marketing", has `instagram_content_publish`, addresses @designerwallcoverings) direct to the Graph API. Norma needs a Steve token re-mint (short-lived Explorer paste → fb_exchange_token) before her agent can post again; per standing rule the secrets token was NOT written into Norma's .env.
+
+**Media hosting:** reel at https://marketing.designerwallcoverings.com/reels/whimsical-wallcoverings-launch-reel-20260723.mp4 (Kamatera :9600 public path). Grid feed images = Shopify CDN on-the-fly 4:5 JPEG transforms of the live product images (`&width=1080&height=1350&crop=center&format=pjpg`); the Slot 5 line card was uploaded to Shopify Files (MediaImage 28595689324595 → https://cdn.shopify.com/s/files/1/0015/4117/7456/files/whimsical-wallcoverings-line-card.jpg?v=1784819162). Local 4:5 masters kept in `marketing/feed/`.
+
+### IG grid queue — arm the daily launchd job (Steve paste)
+
+Posts one queued slot/day at 10:00 (Slot 8 → Slot 1, done ~7/31; each run verifies the permalink back and logs to `scripts/ig-grid-queue.log`):
+
+```sh
+launchctl bootstrap gui/$(id -u) /Users/macstudio3/Projects/wild-wallcoverings/scripts/com.steve.whimsical-ig-grid.plist
+```
+
+To fire one manually instead: `node /Users/macstudio3/Projects/wild-wallcoverings/scripts/ig-grid-post-next.mjs`
+To disarm after the queue completes: `launchctl bootout gui/$(id -u)/com.steve.whimsical-ig-grid`
 
 ## The line
 
diff --git a/marketing/feed/whimsical-slot1.jpg b/marketing/feed/whimsical-slot1.jpg
new file mode 100644
index 0000000..083a94a
Binary files /dev/null and b/marketing/feed/whimsical-slot1.jpg differ
diff --git a/marketing/feed/whimsical-slot2.jpg b/marketing/feed/whimsical-slot2.jpg
new file mode 100644
index 0000000..98c7b7a
Binary files /dev/null and b/marketing/feed/whimsical-slot2.jpg differ
diff --git a/marketing/feed/whimsical-slot3.jpg b/marketing/feed/whimsical-slot3.jpg
new file mode 100644
index 0000000..b41d6ef
Binary files /dev/null and b/marketing/feed/whimsical-slot3.jpg differ
diff --git a/marketing/feed/whimsical-slot4.jpg b/marketing/feed/whimsical-slot4.jpg
new file mode 100644
index 0000000..6aff32b
Binary files /dev/null and b/marketing/feed/whimsical-slot4.jpg differ
diff --git a/marketing/feed/whimsical-slot5.jpg b/marketing/feed/whimsical-slot5.jpg
new file mode 100644
index 0000000..b65860b
Binary files /dev/null and b/marketing/feed/whimsical-slot5.jpg differ
diff --git a/marketing/feed/whimsical-slot6.jpg b/marketing/feed/whimsical-slot6.jpg
new file mode 100644
index 0000000..e9760f8
Binary files /dev/null and b/marketing/feed/whimsical-slot6.jpg differ
diff --git a/marketing/feed/whimsical-slot7.jpg b/marketing/feed/whimsical-slot7.jpg
new file mode 100644
index 0000000..7f0f031
Binary files /dev/null and b/marketing/feed/whimsical-slot7.jpg differ
diff --git a/marketing/feed/whimsical-slot8.jpg b/marketing/feed/whimsical-slot8.jpg
new file mode 100644
index 0000000..b990d24
Binary files /dev/null and b/marketing/feed/whimsical-slot8.jpg differ
diff --git a/marketing/feed/whimsical-slot9.jpg b/marketing/feed/whimsical-slot9.jpg
new file mode 100644
index 0000000..f00f7c6
Binary files /dev/null and b/marketing/feed/whimsical-slot9.jpg differ
diff --git a/scripts/com.steve.whimsical-ig-grid.plist b/scripts/com.steve.whimsical-ig-grid.plist
new file mode 100644
index 0000000..b639c48
--- /dev/null
+++ b/scripts/com.steve.whimsical-ig-grid.plist
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+  <!-- DRAFT — NOT bootstrapped. Steve pastes the bootstrap command from marketing/README.md
+       ("IG grid queue" section) to arm it. Posts ONE queued Whimsical Wallcoverings grid
+       slot per day at 10:00 local; exits quietly once the queue is done (8 posts,
+       so it self-idles after ~2026-07-31; unload it any time after). -->
+  <key>Label</key><string>com.steve.whimsical-ig-grid</string>
+  <key>ProgramArguments</key>
+  <array>
+    <string>/opt/homebrew/bin/node</string>
+    <string>/Users/macstudio3/Projects/wild-wallcoverings/scripts/ig-grid-post-next.mjs</string>
+  </array>
+  <key>StartCalendarInterval</key>
+  <dict>
+    <key>Hour</key><integer>10</integer>
+    <key>Minute</key><integer>0</integer>
+  </dict>
+  <key>StandardOutPath</key><string>/Users/macstudio3/Projects/wild-wallcoverings/scripts/ig-grid-launchd.log</string>
+  <key>StandardErrorPath</key><string>/Users/macstudio3/Projects/wild-wallcoverings/scripts/ig-grid-launchd.log</string>
+</dict>
+</plist>
diff --git a/scripts/ig-grid-post-next.mjs b/scripts/ig-grid-post-next.mjs
new file mode 100644
index 0000000..1088b83
--- /dev/null
+++ b/scripts/ig-grid-post-next.mjs
@@ -0,0 +1,86 @@
+#!/usr/bin/env node
+// Post the NEXT unposted Whimsical Wallcoverings grid slot to Instagram
+// via the Meta Graph Content Publishing API, then VERIFY the permalink
+// resolves before recording success. One post per run (launchd fires daily).
+//
+// Token: META_ACCESS_TOKEN from ~/Projects/secrets-manager/.env (the healthy
+// never-expiring "DW Marketing" app token — NOT Norma's token, which expired
+// 2026-07-20 and needs a Steve re-mint; see marketing/README.md).
+//
+// $0 (local) — Graph API calls are free.
+
+import { readFileSync, writeFileSync, appendFileSync } from 'node:fs';
+import { fileURLToPath } from 'node:url';
+import { dirname, join } from 'node:path';
+import { homedir } from 'node:os';
+
+const HERE = dirname(fileURLToPath(import.meta.url));
+const QUEUE_PATH = join(HERE, 'ig-grid-queue.json');
+const LOG_PATH = join(HERE, 'ig-grid-queue.log');
+const GRAPH = 'https://graph.facebook.com/v21.0';
+
+const log = (m) => {
+  const line = `${new Date().toISOString()} ${m}`;
+  console.log(line);
+  appendFileSync(LOG_PATH, line + '\n');
+};
+
+const envLine = readFileSync(join(homedir(), 'Projects/secrets-manager/.env'), 'utf8')
+  .split('\n').find((l) => l.startsWith('META_ACCESS_TOKEN='));
+if (!envLine) { log('FATAL: META_ACCESS_TOKEN not found in secrets .env'); process.exit(1); }
+const TOKEN = envLine.slice('META_ACCESS_TOKEN='.length).trim();
+
+const q = JSON.parse(readFileSync(QUEUE_PATH, 'utf8'));
+const next = q.queue.find((e) => !e.posted_at);
+if (!next) { log('Queue complete — nothing to post.'); process.exit(0); }
+
+const post = async (path, params) => {
+  const body = new URLSearchParams({ ...params, access_token: TOKEN });
+  const r = await fetch(`${GRAPH}/${path}`, { method: 'POST', body });
+  return r.json();
+};
+const get = async (path, fields) => {
+  const r = await fetch(`${GRAPH}/${path}?fields=${fields}&access_token=${TOKEN}`);
+  return r.json();
+};
+
+try {
+  log(`Posting slot ${next.slot} — ${next.design}`);
+  // sanity: token still valid + image publicly fetchable
+  const me = await get(q.ig_user_id, 'id,username');
+  if (me.error) throw new Error(`Token check failed: ${JSON.stringify(me.error)}`);
+  const head = await fetch(next.image_url, { method: 'HEAD' });
+  if (!head.ok) throw new Error(`Image URL not fetchable (${head.status}): ${next.image_url}`);
+
+  const caption = `${next.caption}\n\n${q.hashtags}`;
+  const container = await post(`${q.ig_user_id}/media`, { image_url: next.image_url, caption });
+  if (!container.id) throw new Error(`Container create failed: ${JSON.stringify(container)}`);
+
+  // publish (retry a few times while the container processes)
+  let pub = null;
+  for (let i = 0; i < 6; i++) {
+    await new Promise((r) => setTimeout(r, 5000));
+    pub = await post(`${q.ig_user_id}/media_publish`, { creation_id: container.id });
+    if (pub.id) break;
+  }
+  if (!pub?.id) throw new Error(`Publish failed: ${JSON.stringify(pub)}`);
+
+  // VERIFY — fetch the permalink back (no "queued looks fine" claims)
+  const media = await get(pub.id, 'id,permalink,media_type,timestamp');
+  if (!media.permalink) throw new Error(`Permalink verify failed: ${JSON.stringify(media)}`);
+
+  // first comment with the product/collection URL
+  const comment = await post(`${pub.id}/comments`, { message: next.first_comment });
+  if (!comment.id) log(`WARN: first comment failed: ${JSON.stringify(comment)}`);
+
+  next.posted_at = new Date().toISOString();
+  next.media_id = media.id;
+  next.permalink = media.permalink;
+  writeFileSync(QUEUE_PATH, JSON.stringify(q, null, 2) + '\n');
+  log(`LIVE slot ${next.slot}: ${media.permalink} (media ${media.id}, comment ${comment.id || 'FAILED'})`);
+  const remaining = q.queue.filter((e) => !e.posted_at).length;
+  log(`${remaining} post(s) remaining in queue.`);
+} catch (err) {
+  log(`ERROR slot ${next.slot}: ${err.message}`);
+  process.exit(1);
+}
diff --git a/scripts/ig-grid-queue.json b/scripts/ig-grid-queue.json
new file mode 100644
index 0000000..217898e
--- /dev/null
+++ b/scripts/ig-grid-queue.json
@@ -0,0 +1,71 @@
+{
+  "note": "Whimsical Wallcoverings IG grid queue. Posted in reverse slot order (newest post = Slot 1 ends top-left). Slot 9 (Regatta Royale) was posted manually 2026-07-23 (media 18104822393278517, https://www.instagram.com/p/DbI8ecNEvn-/). One post per run; launchd fires daily.",
+  "ig_user_id": "17841400781263605",
+  "hashtags": "#wallcoverings #wallcovering #interiordesign #maximalistdecor #playfulinteriors #patternplay #madetoorder #surfacepattern #interiorinspo #designtrade #statementwalls #homedecor #designerwallcoverings",
+  "queue": [
+    {
+      "slot": 8,
+      "design": "Velvet Ray Ballet - Dusty Rose Wash",
+      "image_url": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/ray-shadow-v4-nano.png?v=1784779195&width=1080&height=1350&crop=center&format=pjpg",
+      "caption": "Pattern borrowed from the sea floor: the spotted skin of an eagle ray, washed in dusty rose. Velvet Ray Ballet reads as texture from across the room and as a story up close.",
+      "first_comment": "Shop Velvet Ray Ballet: https://www.designerwallcoverings.com/products/velvet-ray-ballet-dusty-rose-wash-whimsical-wallcoverings",
+      "posted_at": null, "media_id": null, "permalink": null
+    },
+    {
+      "slot": 7,
+      "design": "Cosmodrome Circus - Graphite Static",
+      "image_url": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/galaxy-bots-v1-nano.png?v=1784779160&width=1080&height=1350&crop=center&format=pjpg",
+      "caption": "Robots. In space. On your walls. Cosmodrome Circus in Graphite Static is the maximalist's answer to a boring den. 24\" x 25\" repeat, printed to order.",
+      "first_comment": "Shop Cosmodrome Circus: https://www.designerwallcoverings.com/products/cosmodrome-circus-graphite-static-whimsical-wallcoverings",
+      "posted_at": null, "media_id": null, "permalink": null
+    },
+    {
+      "slot": 6,
+      "design": "Saurian Empire - Fog & Bone",
+      "image_url": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/lost-continent-v4-nano.png?v=1784779172&width=1080&height=1350&crop=center&format=pjpg",
+      "caption": "The Jurassic, redrawn for a very good nursery. Saurian Empire in Fog & Bone keeps its dinosaurs quiet-luxury neutral — bones, ferns, and all. Kids' rooms deserve real design.",
+      "first_comment": "Shop Saurian Empire: https://www.designerwallcoverings.com/products/saurian-empire-fog-bone-whimsical-wallcoverings",
+      "posted_at": null, "media_id": null, "permalink": null
+    },
+    {
+      "slot": 5,
+      "design": "LINE CARD (center anchor)",
+      "image_url": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/whimsical-wallcoverings-line-card.jpg?v=1784819162",
+      "caption": "Introducing Whimsical Wallcoverings — a new house line from Designer Wallcoverings. 21 original designs, drawn flat in a screen-print hand, digitally printed to order on demand. No minimums to dream on: every pattern samples at $4.25. Explore the full collection — link in bio.",
+      "first_comment": "Explore the full collection: https://www.designerwallcoverings.com/collections/whimsical-wallcoverings",
+      "posted_at": null, "media_id": null, "permalink": null
+    },
+    {
+      "slot": 4,
+      "design": "Mushroom Metropolis - Eucalyptus Mist",
+      "image_url": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/fungi-skyline-v1-nano.png?v=1784779157&width=1080&height=1350&crop=center&format=pjpg",
+      "caption": "A city built entirely of mushrooms. Mushroom Metropolis in Eucalyptus Mist is proof that a serious room can keep a sense of humor. Sample it for $4.25 before you commit the wall.",
+      "first_comment": "Shop Mushroom Metropolis: https://www.designerwallcoverings.com/products/mushroom-metropolis-eucalyptus-mist-whimsical-wallcoverings",
+      "posted_at": null, "media_id": null, "permalink": null
+    },
+    {
+      "slot": 3,
+      "design": "Pineapple Palazzo - Noir & Silver",
+      "image_url": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/golden-pineapple-v5-nano.png?v=1784787167&width=1080&height=1350&crop=center&format=pjpg",
+      "caption": "The classic symbol of welcome, gone after-dark. Pineapple Palazzo in Noir & Silver turns an entry hall into an event. $195/roll, printed to order.",
+      "first_comment": "Shop Pineapple Palazzo: https://www.designerwallcoverings.com/products/pineapple-palazzo-noir-silver-whimsical-wallcoverings",
+      "posted_at": null, "media_id": null, "permalink": null
+    },
+    {
+      "slot": 2,
+      "design": "Cumulus Court - Storm & Dove",
+      "image_url": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/dream-clouds-v4-nano.png?v=1784779153&width=1080&height=1350&crop=center&format=pjpg",
+      "caption": "Weather, but make it upholstered. Cumulus Court drifts graphic clouds across a Storm & Dove sky — calm enough for a bedroom, witty enough for a powder room. Printed to order by DW Bespoke Studio.",
+      "first_comment": "Shop Cumulus Court: https://www.designerwallcoverings.com/products/cumulus-court-storm-dove-whimsical-wallcoverings",
+      "posted_at": null, "media_id": null, "permalink": null
+    },
+    {
+      "slot": 1,
+      "design": "Tigris Nocturne - Onyx & Chalk",
+      "image_url": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/astral-tiger-v1-nano.png?v=1784779133&width=1080&height=1350&crop=center&format=pjpg",
+      "caption": "A folk tiger pacing a field of stars. Tigris Nocturne opens the Whimsical Wallcoverings collection — 21 original designs drawn in our own studio and digitally printed to order. Onyx & Chalk, 24\" repeat. Samples $4.25 — link in bio.",
+      "first_comment": "Shop Tigris Nocturne: https://www.designerwallcoverings.com/products/tigris-nocturne-onyx-chalk-whimsical-wallcoverings",
+      "posted_at": null, "media_id": null, "permalink": null
+    }
+  ]
+}

← 8e9f293 Whimsical Wallcoverings launch marketing package: 9:16 reel  ·  back to Wild Wallcoverings  ·  launch channels round 2: Bluesky LIVE, FB blocked (App Revie d2712e1 →