[object Object]

← back to Marketing Command Center

tiktok: align OAuth scope to draft-mode (video.upload,user.info.basic) + add DW Marketing app icon asset

bed8a934082d90948cf64ccb551678f25933c8a2 · 2026-08-12 19:04:25 -0700 · Steve Abrams

Drop video.publish (Direct Post) from the requested TikTok scope — the DW Marketing
app grants only user.info.basic + video.upload (draft mode), so requesting video.publish
would fail OAuth with unauthorized_scope. Draft-mode also fits the human-gated posting
workflow. Adds a generated 1024x1024 on-brand app icon (Chrome-headless render).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

Files touched

Diff

commit bed8a934082d90948cf64ccb551678f25933c8a2
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Wed Aug 12 19:04:25 2026 -0700

    tiktok: align OAuth scope to draft-mode (video.upload,user.info.basic) + add DW Marketing app icon asset
    
    Drop video.publish (Direct Post) from the requested TikTok scope — the DW Marketing
    app grants only user.info.basic + video.upload (draft mode), so requesting video.publish
    would fail OAuth with unauthorized_scope. Draft-mode also fits the human-gated posting
    workflow. Adds a generated 1024x1024 on-brand app icon (Chrome-headless render).
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---
 assets/tiktok-app-icon/dw-marketing-icon-1024.png | Bin 0 -> 371578 bytes
 assets/tiktok-app-icon/icon.html                  |  24 ++++++++++++++++++++++
 assets/tiktok-app-icon/render.js                  |  15 ++++++++++++++
 modules/channels/index.js                         |   7 ++++++-
 4 files changed, 45 insertions(+), 1 deletion(-)

diff --git a/assets/tiktok-app-icon/dw-marketing-icon-1024.png b/assets/tiktok-app-icon/dw-marketing-icon-1024.png
new file mode 100644
index 0000000..cbfc6cc
Binary files /dev/null and b/assets/tiktok-app-icon/dw-marketing-icon-1024.png differ
diff --git a/assets/tiktok-app-icon/icon.html b/assets/tiktok-app-icon/icon.html
new file mode 100644
index 0000000..a0af68f
--- /dev/null
+++ b/assets/tiktok-app-icon/icon.html
@@ -0,0 +1,24 @@
+<!doctype html><html><head><meta charset="utf-8">
+<link href="https://fonts.googleapis.com/css2?family=Cormorant+Garamond:wght@600;700&display=swap" rel="stylesheet">
+<style>
+  html,body{margin:0;padding:0}
+  /* 1024x1024 opaque app icon — the DW Marketing brand mark (style.css .brand .logo) scaled up.
+     Gold gradient ground + ink DW monogram in Cormorant Garamond, with a hairline inner frame. */
+  .icon{
+    width:1024px;height:1024px;box-sizing:border-box;
+    background:linear-gradient(150deg,#c79b60 0%,#b8925a 42%,#9c773f 100%);
+    display:grid;place-items:center;position:relative;overflow:hidden;
+  }
+  /* subtle top-light sheen for depth */
+  .icon::before{content:"";position:absolute;inset:0;
+    background:radial-gradient(700px 520px at 50% -8%, rgba(255,250,240,.45), rgba(255,250,240,0) 60%);}
+  /* hairline inner frame in ink */
+  .frame{position:absolute;inset:78px;border:3px solid rgba(20,17,15,.55);border-radius:64px;}
+  .dw{font-family:"Cormorant Garamond",Georgia,serif;font-weight:700;color:#14110f;
+    font-size:400px;line-height:1;letter-spacing:0;text-shadow:0 6px 18px rgba(20,17,15,.18);
+    position:relative;z-index:2;transform:translateY(-14px);}
+  .tag{position:absolute;bottom:118px;left:0;right:0;text-align:center;z-index:2;
+    font-family:"Cormorant Garamond",Georgia,serif;font-weight:600;color:#14110f;
+    font-size:58px;letter-spacing:14px;text-transform:uppercase;opacity:.82;}
+</style></head>
+<body><div class="icon"><div class="frame"></div><div class="dw">DW</div><div class="tag">Marketing</div></div></body></html>
diff --git a/assets/tiktok-app-icon/render.js b/assets/tiktok-app-icon/render.js
new file mode 100644
index 0000000..5858cbf
--- /dev/null
+++ b/assets/tiktok-app-icon/render.js
@@ -0,0 +1,15 @@
+// Render icon.html to a crisp opaque 1024x1024 PNG using the system Chrome via Playwright.
+const { chromium } = require('/Users/macstudio3/Projects/marketing-command-center/node_modules/playwright-core') ;
+const path = require('path');
+(async () => {
+  const dir = __dirname;
+  const browser = await chromium.launch({ channel: 'chrome', headless: true });
+  const page = await browser.newPage({ viewport: { width: 1024, height: 1024, deviceScaleFactor: 1 } });
+  await page.goto('file://' + path.join(dir, 'icon.html'), { waitUntil: 'domcontentloaded' });
+  await page.evaluate(() => document.fonts.ready);
+  await page.waitForTimeout(600);
+  const el = await page.$('.icon');
+  await el.screenshot({ path: path.join(dir, 'dw-marketing-icon-1024.png'), type: 'png' });
+  await browser.close();
+  console.log('wrote dw-marketing-icon-1024.png');
+})().catch(e => { console.error(e); process.exit(1); });
diff --git a/modules/channels/index.js b/modules/channels/index.js
index 5fd8a79..bdff7d5 100644
--- a/modules/channels/index.js
+++ b/modules/channels/index.js
@@ -181,7 +181,12 @@ const OAUTH = {
     clientSecret: () => env('TIKTOK_CLIENT_SECRET'),
     authUrl: 'https://www.tiktok.com/v2/auth/authorize/',
     tokenUrl: 'https://open.tiktokapis.com/v2/oauth/token/',
-    scope: 'video.publish,video.upload,user.info.basic',
+    // Draft-mode only: the DW Marketing app grants user.info.basic + video.upload
+    // (Content Posting API "Upload to TikTok" = draft the creator finalizes). We do
+    // NOT request video.publish (Direct Post) — it needs a separate stricter audit,
+    // and draft-mode is the right fit for the human-gated posting workflow. Requesting
+    // an ungranted scope would fail the OAuth with unauthorized_scope.
+    scope: 'video.upload,user.info.basic',
     clientParam: 'client_key',
   },
   facebook: {

← de9aaf3 auto-data-snapshot: 2026-08-12T18:36:17 (1 data files) — pub  ·  back to Marketing Command Center  ·  auto-data-snapshot: 2026-08-12T19:06:56 (1 data files) — pub 3fdad13 →