← 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
A assets/tiktok-app-icon/dw-marketing-icon-1024.pngA assets/tiktok-app-icon/icon.htmlA assets/tiktok-app-icon/render.jsM modules/channels/index.js
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 →