← 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
M marketing/README.mdA marketing/feed/whimsical-slot1.jpgA marketing/feed/whimsical-slot2.jpgA marketing/feed/whimsical-slot3.jpgA marketing/feed/whimsical-slot4.jpgA marketing/feed/whimsical-slot5.jpgA marketing/feed/whimsical-slot6.jpgA marketing/feed/whimsical-slot7.jpgA marketing/feed/whimsical-slot8.jpgA marketing/feed/whimsical-slot9.jpgA scripts/com.steve.whimsical-ig-grid.plistA scripts/ig-grid-post-next.mjsA scripts/ig-grid-queue.json
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 →