[object Object]

← back to Dw Marketing Reels

Wire all 35 postable IG accounts as ready in reels console (shared durable META token; no per-account creds)

5363df34179b698ea183ef33b6f6976d4dfabaeb · 2026-08-25 10:44:41 -0700 · Steve Abrams

Files touched

Diff

commit 5363df34179b698ea183ef33b6f6976d4dfabaeb
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Tue Aug 25 10:44:41 2026 -0700

    Wire all 35 postable IG accounts as ready in reels console (shared durable META token; no per-account creds)
---
 data/ig-accounts.json        | 445 +++++++++++++++++++++++++++++++++++++++++--
 scripts/build-ig-accounts.js |  85 +++++++++
 2 files changed, 511 insertions(+), 19 deletions(-)

diff --git a/data/ig-accounts.json b/data/ig-accounts.json
index 68385a9..1ae3468 100644
--- a/data/ig-accounts.json
+++ b/data/ig-accounts.json
@@ -1,65 +1,472 @@
 [
   {
-    "id": "dw",
+    "id": "ig-asseeninhotels",
+    "label": "As Seen in Hotels",
+    "handle": "asseeninhotels",
+    "business": "asseeninhotels · Instagram (Meta Graph)",
+    "ig_user_id": "17841409119139761",
+    "agentUrl": null,
+    "token_source": "shared-meta-durable",
+    "status": "ready",
+    "source": "canonical-postable",
+    "note": "Postable now on the shared durable META token via scripts/publish-ig.mjs — no per-account creds needed."
+  },
+  {
+    "id": "ig-beverlyhillsvideos",
     "label": "Designer Wallcoverings",
+    "handle": "beverlyhillsvideos",
+    "business": "beverlyhillsvideos · Instagram (Meta Graph)",
+    "ig_user_id": "17841408681887144",
+    "agentUrl": null,
+    "token_source": "shared-meta-durable",
+    "status": "ready",
+    "source": "canonical-postable",
+    "note": "Postable now on the shared durable META token via scripts/publish-ig.mjs — no per-account creds needed."
+  },
+  {
+    "id": "ig-borninbeverlyhills",
+    "label": "Born in Beverly Hills",
+    "handle": "borninbeverlyhills",
+    "business": "borninbeverlyhills · Instagram (Meta Graph)",
+    "ig_user_id": "17841408772995866",
+    "agentUrl": null,
+    "token_source": "shared-meta-durable",
+    "status": "ready",
+    "source": "canonical-postable",
+    "note": "Postable now on the shared durable META token via scripts/publish-ig.mjs — no per-account creds needed."
+  },
+  {
+    "id": "ig-brazilliancewallpaper",
+    "label": "Brazilliance Wallpaper and Fabric",
+    "handle": "brazilliancewallpaper",
+    "business": "brazilliancewallpaper · Instagram (Meta Graph)",
+    "ig_user_id": "17841407025318686",
+    "agentUrl": null,
+    "token_source": "shared-meta-durable",
+    "status": "ready",
+    "source": "canonical-postable",
+    "note": "Postable now on the shared durable META token via scripts/publish-ig.mjs — no per-account creds needed."
+  },
+  {
+    "id": "ig-chinoiseriewallpaper",
+    "label": "Et Cie Chinoiserie Wall Murals",
+    "handle": "chinoiseriewallpaper",
+    "business": "chinoiseriewallpaper · Instagram (Meta Graph)",
+    "ig_user_id": "17841407176278551",
+    "agentUrl": null,
+    "token_source": "shared-meta-durable",
+    "status": "ready",
+    "source": "canonical-postable",
+    "note": "Postable now on the shared durable META token via scripts/publish-ig.mjs — no per-account creds needed."
+  },
+  {
+    "id": "ig-commercialwallcoverings",
+    "label": "Commercial Wallcoverings",
+    "handle": "commercialwallcoverings",
+    "business": "commercialwallcoverings · Instagram (Meta Graph)",
+    "ig_user_id": "17841408775091102",
+    "agentUrl": null,
+    "token_source": "shared-meta-durable",
+    "status": "ready",
+    "source": "canonical-postable",
+    "note": "Postable now on the shared durable META token via scripts/publish-ig.mjs — no per-account creds needed."
+  },
+  {
+    "id": "ig-customwallcoverings",
+    "label": "Custom Wall Papers and Wallcoverings",
+    "handle": "customwallcoverings",
+    "business": "customwallcoverings · Instagram (Meta Graph)",
+    "ig_user_id": "17841408685240867",
+    "agentUrl": null,
+    "token_source": "shared-meta-durable",
+    "status": "ready",
+    "source": "canonical-postable",
+    "note": "Postable now on the shared durable META token via scripts/publish-ig.mjs — no per-account creds needed."
+  },
+  {
+    "id": "ig-designerlaboratory",
+    "label": "Designer Laboratory",
+    "handle": "designerlaboratory",
+    "business": "designerlaboratory · Instagram (Meta Graph)",
+    "ig_user_id": "17841405610219019",
+    "agentUrl": null,
+    "token_source": "shared-meta-durable",
+    "status": "ready",
+    "source": "canonical-postable",
+    "note": "Postable now on the shared durable META token via scripts/publish-ig.mjs — no per-account creds needed."
+  },
+  {
+    "id": "dw",
+    "label": "DesignerWallcoverings.com",
     "handle": "designerwallcoverings",
-    "business": "designerwallcoverings.com",
-    "agentUrl": "http://127.0.0.1:9810",
+    "business": "designerwallcoverings · Instagram (Meta Graph)",
+    "ig_user_id": "17841400781263605",
+    "agentUrl": null,
+    "token_source": "shared-meta-durable",
+    "status": "ready",
+    "source": "canonical-postable",
+    "note": "Postable now on the shared durable META token via scripts/publish-ig.mjs — no per-account creds needed."
+  },
+  {
+    "id": "ig-fabric-fridays",
+    "label": "Fabric Fridays",
+    "handle": "fabric_fridays",
+    "business": "fabric_fridays · Instagram (Meta Graph)",
+    "ig_user_id": "17841434885415391",
+    "agentUrl": null,
+    "token_source": "shared-meta-durable",
+    "status": "ready",
+    "source": "canonical-postable",
+    "note": "Postable now on the shared durable META token via scripts/publish-ig.mjs — no per-account creds needed."
+  },
+  {
+    "id": "ig-ffepurchasing",
+    "label": "FFE Purchasing",
+    "handle": "ffepurchasing",
+    "business": "ffepurchasing · Instagram (Meta Graph)",
+    "ig_user_id": "17841407058348607",
+    "agentUrl": null,
+    "token_source": "shared-meta-durable",
+    "status": "ready",
+    "source": "canonical-postable",
+    "note": "Postable now on the shared durable META token via scripts/publish-ig.mjs — no per-account creds needed."
+  },
+  {
+    "id": "ig-goldleafwallpaper",
+    "label": "Gold Leaf Wallpaper",
+    "handle": "goldleafwallpaper",
+    "business": "goldleafwallpaper · Instagram (Meta Graph)",
+    "ig_user_id": "17841408856660866",
+    "agentUrl": null,
+    "token_source": "shared-meta-durable",
+    "status": "ready",
+    "source": "canonical-postable",
+    "note": "Postable now on the shared durable META token via scripts/publish-ig.mjs — no per-account creds needed."
+  },
+  {
+    "id": "ig-grassclothwallpaper",
+    "label": "Wallpaper Weekly",
+    "handle": "grassclothwallpaper",
+    "business": "grassclothwallpaper · Instagram (Meta Graph)",
+    "ig_user_id": "17841408868631110",
+    "agentUrl": null,
+    "token_source": "shared-meta-durable",
+    "status": "ready",
+    "source": "canonical-postable",
+    "note": "Postable now on the shared durable META token via scripts/publish-ig.mjs — no per-account creds needed."
+  },
+  {
+    "id": "ig-hollywoodwallcoverings",
+    "label": "Hollywood Wallcoverings",
+    "handle": "hollywoodwallcoverings",
+    "business": "hollywoodwallcoverings · Instagram (Meta Graph)",
+    "ig_user_id": "17841406772943912",
+    "agentUrl": null,
+    "token_source": "shared-meta-durable",
     "status": "ready",
-    "note": "Main DW account via Norma's instagram-agent (Meta Graph). Creds live on the publish host."
+    "source": "canonical-postable",
+    "note": "Postable now on the shared durable META token via scripts/publish-ig.mjs — no per-account creds needed."
+  },
+  {
+    "id": "ig-hospitalitywallcoverings",
+    "label": "Hospitality Wallpaper",
+    "handle": "hospitalitywallcoverings",
+    "business": "hospitalitywallcoverings · Instagram (Meta Graph)",
+    "ig_user_id": "17841407188578748",
+    "agentUrl": null,
+    "token_source": "shared-meta-durable",
+    "status": "ready",
+    "source": "canonical-postable",
+    "note": "Postable now on the shared durable META token via scripts/publish-ig.mjs — no per-account creds needed."
+  },
+  {
+    "id": "ig-interiordesignervideos",
+    "label": "Interior Designer Videos",
+    "handle": "interiordesignervideos",
+    "business": "interiordesignervideos · Instagram (Meta Graph)",
+    "ig_user_id": "17841408790905574",
+    "agentUrl": null,
+    "token_source": "shared-meta-durable",
+    "status": "ready",
+    "source": "canonical-postable",
+    "note": "Postable now on the shared durable META token via scripts/publish-ig.mjs — no per-account creds needed."
+  },
+  {
+    "id": "ig-linenwallpaper",
+    "label": "Linen Wallpaper",
+    "handle": "linenwallpaper",
+    "business": "linenwallpaper · Instagram (Meta Graph)",
+    "ig_user_id": "17841408700186822",
+    "agentUrl": null,
+    "token_source": "shared-meta-durable",
+    "status": "ready",
+    "source": "canonical-postable",
+    "note": "Postable now on the shared durable META token via scripts/publish-ig.mjs — no per-account creds needed."
+  },
+  {
+    "id": "ig-metallicwallpaper",
+    "label": "Metallic Wallpaper",
+    "handle": "metallicwallpaper",
+    "business": "metallicwallpaper · Instagram (Meta Graph)",
+    "ig_user_id": "17841408654197157",
+    "agentUrl": null,
+    "token_source": "shared-meta-durable",
+    "status": "ready",
+    "source": "canonical-postable",
+    "note": "Postable now on the shared durable META token via scripts/publish-ig.mjs — no per-account creds needed."
+  },
+  {
+    "id": "ig-papelestapiz",
+    "label": "Papel Tapiz",
+    "handle": "papelestapiz",
+    "business": "papelestapiz · Instagram (Meta Graph)",
+    "ig_user_id": "17841407051208925",
+    "agentUrl": null,
+    "token_source": "shared-meta-durable",
+    "status": "ready",
+    "source": "canonical-postable",
+    "note": "Postable now on the shared durable META token via scripts/publish-ig.mjs — no per-account creds needed."
+  },
+  {
+    "id": "ig-patterndesignlab",
+    "label": "Pattern Design Lab",
+    "handle": "patterndesignlab",
+    "business": "patterndesignlab · Instagram (Meta Graph)",
+    "ig_user_id": "17841405752089037",
+    "agentUrl": null,
+    "token_source": "shared-meta-durable",
+    "status": "ready",
+    "source": "canonical-postable",
+    "note": "Postable now on the shared durable META token via scripts/publish-ig.mjs — no per-account creds needed."
+  },
+  {
+    "id": "ig-philliperomanodesigns",
+    "label": "Phillipe Romano",
+    "handle": "philliperomanodesigns",
+    "business": "philliperomanodesigns · Instagram (Meta Graph)",
+    "ig_user_id": "17841406249858383",
+    "agentUrl": null,
+    "token_source": "shared-meta-durable",
+    "status": "ready",
+    "source": "canonical-postable",
+    "note": "Postable now on the shared durable META token via scripts/publish-ig.mjs — no per-account creds needed."
+  },
+  {
+    "id": "ig-restorationwallpaper",
+    "label": "Restoration Wallpaper",
+    "handle": "restorationwallpaper",
+    "business": "restorationwallpaper · Instagram (Meta Graph)",
+    "ig_user_id": "17841408840359842",
+    "agentUrl": null,
+    "token_source": "shared-meta-durable",
+    "status": "ready",
+    "source": "canonical-postable",
+    "note": "Postable now on the shared durable META token via scripts/publish-ig.mjs — no per-account creds needed."
+  },
+  {
+    "id": "ig-retrowalls",
+    "label": "Retro Walls",
+    "handle": "retrowalls",
+    "business": "retrowalls · Instagram (Meta Graph)",
+    "ig_user_id": "17841406464478261",
+    "agentUrl": null,
+    "token_source": "shared-meta-durable",
+    "status": "ready",
+    "source": "canonical-postable",
+    "note": "Postable now on the shared durable META token via scripts/publish-ig.mjs — no per-account creds needed."
+  },
+  {
+    "id": "ig-roomsettings",
+    "label": "Room Settings",
+    "handle": "roomsettings",
+    "business": "roomsettings · Instagram (Meta Graph)",
+    "ig_user_id": "17841408698848037",
+    "agentUrl": null,
+    "token_source": "shared-meta-durable",
+    "status": "ready",
+    "source": "canonical-postable",
+    "note": "Postable now on the shared durable META token via scripts/publish-ig.mjs — no per-account creds needed."
+  },
+  {
+    "id": "ig-screenprintedwallpaper",
+    "label": "Screen Printed Wallpaper",
+    "handle": "screenprintedwallpaper",
+    "business": "screenprintedwallpaper · Instagram (Meta Graph)",
+    "ig_user_id": "17841408660869742",
+    "agentUrl": null,
+    "token_source": "shared-meta-durable",
+    "status": "ready",
+    "source": "canonical-postable",
+    "note": "Postable now on the shared durable META token via scripts/publish-ig.mjs — no per-account creds needed."
+  },
+  {
+    "id": "ig-sheltermagazines",
+    "label": "Shelter Magazines",
+    "handle": "sheltermagazines",
+    "business": "sheltermagazines · Instagram (Meta Graph)",
+    "ig_user_id": "17841406881694118",
+    "agentUrl": null,
+    "token_source": "shared-meta-durable",
+    "status": "ready",
+    "source": "canonical-postable",
+    "note": "Postable now on the shared durable META token via scripts/publish-ig.mjs — no per-account creds needed."
+  },
+  {
+    "id": "ig-suedewallpaper",
+    "label": "Suede Wallpaper",
+    "handle": "suedewallpaper",
+    "business": "suedewallpaper · Instagram (Meta Graph)",
+    "ig_user_id": "17841408648437136",
+    "agentUrl": null,
+    "token_source": "shared-meta-durable",
+    "status": "ready",
+    "source": "canonical-postable",
+    "note": "Postable now on the shared durable META token via scripts/publish-ig.mjs — no per-account creds needed."
+  },
+  {
+    "id": "ig-textilewallpaper",
+    "label": "Textile Wallpaper",
+    "handle": "textilewallpaper",
+    "business": "textilewallpaper · Instagram (Meta Graph)",
+    "ig_user_id": "17841408813467105",
+    "agentUrl": null,
+    "token_source": "shared-meta-durable",
+    "status": "ready",
+    "source": "canonical-postable",
+    "note": "Postable now on the shared durable META token via scripts/publish-ig.mjs — no per-account creds needed."
+  },
+  {
+    "id": "ig-thedesignerlibrary",
+    "label": "Designer Library",
+    "handle": "thedesignerlibrary",
+    "business": "thedesignerlibrary · Instagram (Meta Graph)",
+    "ig_user_id": "17841408572952750",
+    "agentUrl": null,
+    "token_source": "shared-meta-durable",
+    "status": "ready",
+    "source": "canonical-postable",
+    "note": "Postable now on the shared durable META token via scripts/publish-ig.mjs — no per-account creds needed."
+  },
+  {
+    "id": "ig-thesetdecorator",
+    "label": "The Set Decorator",
+    "handle": "thesetdecorator",
+    "business": "thesetdecorator · Instagram (Meta Graph)",
+    "ig_user_id": "17841406774743877",
+    "agentUrl": null,
+    "token_source": "shared-meta-durable",
+    "status": "ready",
+    "source": "canonical-postable",
+    "note": "Postable now on the shared durable META token via scripts/publish-ig.mjs — no per-account creds needed."
+  },
+  {
+    "id": "ig-traditionalwhimsy",
+    "label": "Traditional Whimsy",
+    "handle": "traditionalwhimsy",
+    "business": "traditionalwhimsy · Instagram (Meta Graph)",
+    "ig_user_id": "17841408645959795",
+    "agentUrl": null,
+    "token_source": "shared-meta-durable",
+    "status": "ready",
+    "source": "canonical-postable",
+    "note": "Postable now on the shared durable META token via scripts/publish-ig.mjs — no per-account creds needed."
+  },
+  {
+    "id": "ig-velvetwallpaper",
+    "label": "Velvet Wallpaper",
+    "handle": "velvetwallpaper",
+    "business": "velvetwallpaper · Instagram (Meta Graph)",
+    "ig_user_id": "17841408844140138",
+    "agentUrl": null,
+    "token_source": "shared-meta-durable",
+    "status": "ready",
+    "source": "canonical-postable",
+    "note": "Postable now on the shared durable META token via scripts/publish-ig.mjs — no per-account creds needed."
+  },
+  {
+    "id": "ig-wallpaperhistory",
+    "label": "Wallpaper History",
+    "handle": "wallpaperhistory",
+    "business": "wallpaperhistory · Instagram (Meta Graph)",
+    "ig_user_id": "17841408790217103",
+    "agentUrl": null,
+    "token_source": "shared-meta-durable",
+    "status": "ready",
+    "source": "canonical-postable",
+    "note": "Postable now on the shared durable META token via scripts/publish-ig.mjs — no per-account creds needed."
+  },
+  {
+    "id": "ig-wallpaperinstallers",
+    "label": "Wallpaper Installers",
+    "handle": "wallpaperinstallers",
+    "business": "wallpaperinstallers · Instagram (Meta Graph)",
+    "ig_user_id": "17841414900195265",
+    "agentUrl": null,
+    "token_source": "shared-meta-durable",
+    "status": "ready",
+    "source": "canonical-postable",
+    "note": "Postable now on the shared durable META token via scripts/publish-ig.mjs — no per-account creds needed."
+  },
+  {
+    "id": "ig-wallpaperwednesdays",
+    "label": "Wallpaper Wednesdays",
+    "handle": "wallpaperwednesdays",
+    "business": "wallpaperwednesdays · Instagram (Meta Graph)",
+    "ig_user_id": "17841409211532596",
+    "agentUrl": null,
+    "token_source": "shared-meta-durable",
+    "status": "ready",
+    "source": "canonical-postable",
+    "note": "Postable now on the shared durable META token via scripts/publish-ig.mjs — no per-account creds needed."
   },
   {
     "id": "phillipe-romano",
     "label": "Phillipe Romano",
     "handle": "philipperomano",
     "business": "philipperomano.com",
+    "note": "Aspirational @philipperomano — no IG account/token confirmed. (The active postable account for this brand is @philliperomanodesigns, listed separately.)",
     "agentUrl": null,
     "status": "pending-creds",
-    "note": "Handle + Meta creds TBD — set handle and wire creds to activate."
+    "source": "aspirational"
   },
   {
     "id": "apartment-wallpaper",
     "label": "Apartment Wallpaper",
     "handle": null,
     "business": "apartmentwallpaper.com",
+    "note": "Peel-and-stick line. No Instagram account yet — create + link to the Meta Business to activate.",
     "agentUrl": null,
     "status": "pending-creds",
-    "note": "Peel-and-stick line. Handle + creds TBD."
+    "source": "aspirational"
   },
   {
     "id": "novasuede",
     "label": "Novasuede",
     "handle": null,
     "business": "novasuede.com",
+    "note": "No Instagram account yet — create + link to the Meta Business to activate.",
     "agentUrl": null,
     "status": "pending-creds",
-    "note": "Handle + creds TBD."
+    "source": "aspirational"
   },
   {
     "id": "architectural-wallcoverings",
     "label": "Architectural Wallcoverings",
     "handle": null,
     "business": "architecturalwallcoverings.com",
+    "note": "Contract/commercial line. No Instagram account yet — create + link to the Meta Business to activate.",
     "agentUrl": null,
     "status": "pending-creds",
-    "note": "Contract/commercial line. Handle + creds TBD."
-  },
-  {
-    "id": "hollywood-wallcoverings",
-    "label": "Hollywood Wallcoverings",
-    "handle": null,
-    "business": "hollywood-wallcoverings",
-    "agentUrl": null,
-    "status": "pending-creds",
-    "note": "Private-label line (never name the upstream vendor publicly). Handle + creds TBD."
+    "source": "aspirational"
   },
   {
     "id": "malibu-wallpaper",
     "label": "Malibu Wallpaper",
     "handle": null,
     "business": "malibu wallpaper line",
+    "note": "Private-label line (never name the upstream vendor publicly). No Instagram account yet — create + link to the Meta Business to activate.",
     "agentUrl": null,
     "status": "pending-creds",
-    "note": "Private-label line (never name the upstream vendor publicly). Handle + creds TBD."
+    "source": "aspirational"
   }
-]
\ No newline at end of file
+]
diff --git a/scripts/build-ig-accounts.js b/scripts/build-ig-accounts.js
new file mode 100644
index 0000000..e46a9d9
--- /dev/null
+++ b/scripts/build-ig-accounts.js
@@ -0,0 +1,85 @@
+#!/usr/bin/env node
+/*
+ * build-ig-accounts.js — regenerate data/ig-accounts.json, the "managed" account
+ * layer the Reels console reads (server.js → readAccountsUnion).
+ *
+ * WHY THIS EXISTS (2026-08-25): the console showed ~35 owned Instagram accounts as
+ * "(needs creds)" even though they are ALL postable RIGHT NOW. Norma's registry
+ * (accounts.json) reports token_source = META_ACCESS_TOKEN (shared, never-expiring),
+ * accounts_postable = 35 — one durable page token authorizes every one of those 35
+ * IG accounts. There are no per-account credentials to acquire. The only reason they
+ * rendered "(needs creds)" is that readAccountsUnion() forces every canonical-only
+ * account to status:'pending-creds' unless it has an explicit managed entry here, and
+ * this file previously listed only the main `dw` account as ready.
+ *
+ * This generator promotes every postable canonical account (data/dw-canonical-accounts.json,
+ * mirrored from Norma by build-dw-accounts.js) to status:'ready' with its ig_user_id, so
+ * publish-ig.mjs can post to any of them on the shared token. Accounts that have NO real
+ * Instagram account yet (aspirational brands) are preserved as pending-creds via the
+ * ASPIRATIONAL manifest below — those genuinely need an IG Business account created +
+ * linked to the Meta Business before they can go live.
+ *
+ * Idempotent. Runs on Mac2 (where Norma lives). The OUT file is committed so it ships
+ * to Kamatera.  →  node scripts/build-ig-accounts.js
+ */
+import { readFileSync, writeFileSync, existsSync, renameSync } from 'node:fs';
+import { join, dirname } from 'node:path';
+import { fileURLToPath } from 'node:url';
+
+const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..');
+const CANON = join(ROOT, 'data', 'dw-canonical-accounts.json');
+const OUT = join(ROOT, 'data', 'ig-accounts.json');
+
+// Brands with NO postable Instagram account yet (handle NOT among the 35 postable).
+// These stay pending-creds — the gated work is: create an IG Business account + link
+// it to the Meta Business so the shared token covers it. Keyed by handle (or a synthetic
+// id when there is no handle at all).
+const ASPIRATIONAL = [
+  { id: 'phillipe-romano', label: 'Phillipe Romano', handle: 'philipperomano', business: 'philipperomano.com',
+    note: 'Aspirational @philipperomano — no IG account/token confirmed. (The active postable account for this brand is @philliperomanodesigns, listed separately.)' },
+  { id: 'apartment-wallpaper', label: 'Apartment Wallpaper', handle: null, business: 'apartmentwallpaper.com',
+    note: 'Peel-and-stick line. No Instagram account yet — create + link to the Meta Business to activate.' },
+  { id: 'novasuede', label: 'Novasuede', handle: null, business: 'novasuede.com',
+    note: 'No Instagram account yet — create + link to the Meta Business to activate.' },
+  { id: 'architectural-wallcoverings', label: 'Architectural Wallcoverings', handle: null, business: 'architecturalwallcoverings.com',
+    note: 'Contract/commercial line. No Instagram account yet — create + link to the Meta Business to activate.' },
+  { id: 'malibu-wallpaper', label: 'Malibu Wallpaper', handle: null, business: 'malibu wallpaper line',
+    note: 'Private-label line (never name the upstream vendor publicly). No Instagram account yet — create + link to the Meta Business to activate.' },
+];
+
+if (!existsSync(CANON)) {
+  console.error('✗ canonical source missing:', CANON, '— run scripts/build-dw-accounts.js first');
+  process.exit(1);
+}
+const canon = JSON.parse(readFileSync(CANON, 'utf8'));
+const postable = (canon.accounts || []).filter(a => a && a.handle);
+
+// The main DW account keeps its historical id `dw` (other tooling references it).
+const idFor = handle => handle === 'designerwallcoverings'
+  ? 'dw'
+  : 'ig-' + String(handle).toLowerCase().replace(/[^a-z0-9-]+/g, '-');
+
+const ready = postable.map(a => ({
+  id: idFor(a.handle),
+  label: a.name || a.handle,
+  handle: a.handle,
+  business: `${a.handle} · Instagram (Meta Graph)`,
+  ig_user_id: a.ig_user_id || null,
+  agentUrl: null,                       // published via scripts/publish-ig.mjs, not the HTTP agent
+  token_source: 'shared-meta-durable',  // one never-expiring META page token covers all 35
+  status: a.ig_user_id ? 'ready' : 'pending-creds',
+  source: 'canonical-postable',
+  note: 'Postable now on the shared durable META token via scripts/publish-ig.mjs — no per-account creds needed.',
+}));
+
+// Keep aspirational placeholders that don't collide (by handle) with a postable account.
+const postableHandles = new Set(postable.map(a => a.handle.toLowerCase()));
+const pending = ASPIRATIONAL
+  .filter(a => !(a.handle && postableHandles.has(a.handle.toLowerCase())))
+  .map(a => ({ ...a, agentUrl: null, status: 'pending-creds', source: 'aspirational' }));
+
+const out = [...ready, ...pending];
+const tmp = OUT + '.tmp';
+writeFileSync(tmp, JSON.stringify(out, null, 2) + '\n');
+renameSync(tmp, OUT);
+console.log(`wrote data/ig-accounts.json — ${ready.length} postable (ready) + ${pending.length} aspirational (pending-creds)`);

← 276feb4 reels UI: Post to YouTube button on each studio video chip  ·  back to Dw Marketing Reels  ·  publish-ig.mjs: resolve per-account ig_user_id (--account / b778dff →