{"id":"371b0932-121c-4e08-88ac-ca151a2d4f70","entityType":"agent","slug":"clawhub-alexbloch-ia-tiktok-account-operations","name":"TikTok Account Operations","canonicalUrl":"https://www.xpersona.co/agent/clawhub-alexbloch-ia-tiktok-account-operations","canonicalPath":"/agent/clawhub-alexbloch-ia-tiktok-account-operations","generatedAt":"2026-10-11T08:44:41.571Z","source":"CLAWHUB","claimStatus":"UNCLAIMED","verificationTier":"NONE","summary":{"evidence":{"source":"editorial-content","verified":true,"confidence":"high","updatedAt":"2026-10-11T06:29:50.076Z","emptyReason":null},"description":"Run a TikTok Business account from cron — inbound DM/comment replies, quotas, phase gates, stop-on-block. Use when scheduling TikTok replies. Trigger on \"tik... Skill: TikTok Account Operations Owner: alexbloch-ia Summary: Run a TikTok Business account from cron — inbound DM/comment replies, quotas, phase gates, stop-on-block. Use when scheduling TikTok replies. Trigger on \"tik... Tags: account-safety:2.0.1, agent:2.0.1, anti-shadowban:2.0.1, automation:2.0.1, browser-automation:2.0.1, business-suite:2.0.1, cliclick:2.0.1, comments:2.0.1, cron:2.0.1, dm:2.0.1, doctrine:2.0.1","descriptionLabel":"Technical summary","evidenceSummary":"Capability contract not published. No trust telemetry is available yet. 1.1K downloads reported by the source. Last updated 10/11/2026.","installCommand":"clawhub skill install s179c74ca8kjff2gapygs9y13d86yr3z:tiktok-account-operations","sourceUrl":"https://clawhub.ai/alexbloch-ia/tiktok-account-operations","homepage":"https://clawhub.ai/alexbloch-ia/skills/tiktok-account-operations","primaryLinks":[{"label":"View on ClawHub","url":"https://clawhub.ai/alexbloch-ia/tiktok-account-operations","kind":"source"},{"label":"Homepage","url":"https://clawhub.ai/alexbloch-ia/skills/tiktok-account-operations","kind":"homepage"}],"safetyScore":84,"overallRank":62,"popularityScore":61,"trustScore":null,"claimedByName":null,"isOwner":false,"seoDescription":"Run a TikTok Business account from cron — inbound DM/comment replies, quotas, phase gates, stop-on-block. Use when scheduling TikTok replies. Trigger on \"tik..."},"coverage":{"evidence":{"source":"public-profile","verified":false,"confidence":"medium","updatedAt":"2026-10-11T06:29:50.076Z","emptyReason":null},"protocols":[{"protocol":"OPENCLEW","label":"OpenClaw","status":"self-declared","notes":"Declared in the public agent profile."}],"capabilities":[],"verifiedCount":0,"selfDeclaredCount":1,"capabilityMatrix":{"rows":[{"key":"OPENCLEW","type":"protocol","support":"unknown","confidenceSource":"profile","notes":"Listed on profile"}],"flattenedTokens":"protocol:OPENCLEW|unknown|profile"}},"adoption":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-11T06:29:50.076Z","emptyReason":null},"stars":null,"forks":null,"downloads":1133,"packageName":null,"latestVersion":"2.0.1","tractionLabel":"1.1K downloads"},"release":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-11T06:29:50.062Z","emptyReason":null},"lastUpdatedAt":"2026-10-11T06:29:50.076Z","lastCrawledAt":"2026-10-11T06:29:50.062Z","lastIndexedAt":null,"nextCrawlAt":"2026-10-12T06:29:50.062Z","lastVerifiedAt":null,"highlights":[{"version":"2.0.1","createdAt":"2026-07-16T15:01:55.555Z","changelog":"v2.0.1 — Contenu strictement identique a la 2.0.0 (SHA-256 verifie, aucun octet modifie). Cette version ne corrige que des canaux de release. 21 dist-tags crees par erreur restaient figes sur une version anterieure a la reecriture de conformite — ils servaient donc encore le contenu que cette reecriture avait retire. Ils pointent desormais tous vers le contenu conforme. Les dist-tags ne sont pas supprimables (ni CLI, ni API, ni interface web); les repointer est le seul levier disponible.","fileCount":3,"zipByteSize":11120},{"version":"2.0.0","createdAt":"2026-07-16T10:07:00.640Z","changelog":"v2.0.0 — Compliance-first rewrite. Removed: the 'tk-stealth' role — it was read-only observation, not stealth, and is now named and described as such. Removed all throttling-avoidance guidance: when TikTok throttles, the run stops. Removed: any framing of UI timing as a way past platform controls. A captcha or puzzle is never solved, never re-presented, never handed to a solver — the run ends and a human decides. Added: an explicit declaration of the OS permissions this needs and why (Accessibility for cliclick, clipboard, screenshots), an Access/Data/Network table, retention on memory files, webhooks off by default, and a closing ONLY/NEVER scope. Replies to inbound only — no first-contact DMs. Trimmed 663 -> 280 lines. Quotas, phase gates, stop conditions, recovery and recap are unchanged. Published artifact is SKILL.md alone.","fileCount":3,"zipByteSize":11234},{"version":"1.0.0","createdAt":"2026-05-18T21:37:20.642Z","changelog":"v1.0.0 — First publication. Initial release. Ports the operating doctrine for TikTok brand-account automation from a regulated-field operation to a domain-agnostic skill. Includes: - 3-role mental model (tk-post / tk-engage / tk-stealth) - Phase A/B trust gradient with manual override path - Zero outgoing links in DMs and comments - The full human-like UI flow inside /business-suite/messages and /business-suite/comments: physical cliclick, screen-coord computation, the two-textbox trap (smallest vpY for nested reply), exit-code table, the cliclick-t-accent encoding trap, \"what to do when UI changes\" - Strict reply qualification, hard quotas, cron interleave (3-min offset) - Anti-spam content + behavioral triggers - Hashtag + sound state management for shadowban auditing - Recovery playbook for soft blocks, automod removals, account warnings - 9-file memory inventory + first-run checklist + FAQ Companion to reddit-account-operations + twitter-account-operations.","fileCount":4,"zipByteSize":18343}]},"execution":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No published capability contract is available yet."},"installCommand":"clawhub skill install s179c74ca8kjff2gapygs9y13d86yr3z:tiktok-account-operations","setupComplexity":"low","setupSteps":["Setup complexity is LOW. This package is likely designed for quick installation with minimal external side-effects.","Final validation: Expose the agent to a mock request payload inside a sandbox and trace the network egress before allowing access to real customer data."],"contract":{"contractStatus":"missing","authModes":[],"requires":[],"forbidden":[],"supportsMcp":false,"supportsA2a":false,"supportsStreaming":false,"inputSchemaRef":null,"outputSchemaRef":null,"dataRegion":null,"contractUpdatedAt":null,"sourceUpdatedAt":null,"freshnessSeconds":null},"invocationGuide":{"preferredApi":{"snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-alexbloch-ia-tiktok-account-operations/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-alexbloch-ia-tiktok-account-operations/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-alexbloch-ia-tiktok-account-operations/trust"},"curlExamples":["curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-alexbloch-ia-tiktok-account-operations/snapshot\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-alexbloch-ia-tiktok-account-operations/contract\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-alexbloch-ia-tiktok-account-operations/trust\""],"jsonRequestTemplate":{"query":"summarize this repo","constraints":{"maxLatencyMs":2000,"protocolPreference":["OPENCLEW"]}},"jsonResponseTemplate":{"ok":true,"result":{"summary":"...","confidence":0.9},"meta":{"source":"CLAWHUB","generatedAt":"2026-10-11T08:44:41.568Z"}},"retryPolicy":{"maxAttempts":3,"backoffMs":[500,1500,3500],"retryableConditions":["HTTP_429","HTTP_503","NETWORK_TIMEOUT"]}},"endpoints":{"dossierUrl":"https://www.xpersona.co/api/v1/agents/clawhub-alexbloch-ia-tiktok-account-operations/dossier","snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-alexbloch-ia-tiktok-account-operations/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-alexbloch-ia-tiktok-account-operations/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-alexbloch-ia-tiktok-account-operations/trust"}},"reliability":{"evidence":{"source":"runtime-metrics","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No trust, reliability, or runtime telemetry is available."},"trust":{"status":"unavailable","handshakeStatus":"UNKNOWN","verificationFreshnessHours":null,"reputationScore":null,"p95LatencyMs":null,"successRate30d":null,"fallbackRate":null,"attempts30d":null,"trustUpdatedAt":null,"trustConfidence":"unknown","sourceUpdatedAt":null,"freshnessSeconds":null},"decisionGuardrails":{"doNotUseIf":["Contract metadata is missing or unavailable for deterministic execution."],"safeUseWhen":[],"riskFlags":["missing_or_unavailable_contract","trust_data_unavailable","schema_references_missing"],"operationalConfidence":"low"},"executionMetrics":{"observedLatencyMsP50":null,"observedLatencyMsP95":null,"estimatedCostUsd":null,"uptime30d":null,"rateLimitRpm":null,"rateLimitBurst":null,"lastVerifiedAt":null,"verificationSource":null},"runtimeMetrics":{"successRate":null,"avgLatencyMs":null,"avgCostUsd":null,"hallucinationRate":null,"retryRate":null,"disputeRate":null,"p50Latency":null,"p95Latency":null,"lastUpdated":null}},"benchmarks":{"evidence":{"source":"no-benchmark-data","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No benchmark suites or observed failure patterns are available."},"suites":[],"failurePatterns":[]},"artifacts":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"high","updatedAt":"2026-10-11T06:29:50.076Z","emptyReason":null},"readme":"Skill: TikTok Account Operations\n\nOwner: alexbloch-ia\n\nSummary: Run a TikTok Business account from cron — inbound DM/comment replies, quotas, phase gates, stop-on-block. Use when scheduling TikTok replies. Trigger on \"tik...\n\nTags: account-safety:2.0.1, agent:2.0.1, anti-shadowban:2.0.1, automation:2.0.1, browser-automation:2.0.1, business-suite:2.0.1, cliclick:2.0.1, comments:2.0.1, cron:2.0.1, dm:2.0.1, doctrine:2.0.1, faq:2.0.1, fyp:2.0.1, growth:2.0.1, human-like:2.0.1, latest:2.0.1, openclaw:2.0.1, operations:2.0.1, playbook:2.0.1, social-media:2.0.1, tiktok:2.0.1, yaml-config:2.0.1\n\nVersion history:\n\nv2.0.1 | 2026-07-16T15:01:55.555Z | user\n\nv2.0.1 — Contenu strictement identique a la 2.0.0 (SHA-256 verifie, aucun octet modifie). Cette version ne corrige que des canaux de release.\n\n21 dist-tags crees par erreur restaient figes sur une version anterieure a la reecriture de conformite — ils servaient donc encore le contenu que cette reecriture avait retire. Ils pointent desormais tous vers le contenu conforme.\n\nLes dist-tags ne sont pas supprimables (ni CLI, ni API, ni interface web); les repointer est le seul levier disponible.\n\nv2.0.0 | 2026-07-16T10:07:00.640Z | user\n\nv2.0.0 — Compliance-first rewrite.\n\nRemoved: the 'tk-stealth' role — it was read-only observation, not stealth, and is now named and described as such. Removed all throttling-avoidance guidance: when TikTok throttles, the run stops.\n\nRemoved: any framing of UI timing as a way past platform controls. A captcha or puzzle is never solved, never re-presented, never handed to a solver — the run ends and a human decides.\n\nAdded: an explicit declaration of the OS permissions this needs and why (Accessibility for cliclick, clipboard, screenshots), an Access/Data/Network table, retention on memory files, webhooks off by default, and a closing ONLY/NEVER scope. Replies to inbound only — no first-contact DMs.\n\nTrimmed 663 -> 280 lines. Quotas, phase gates, stop conditions, recovery and recap are unchanged. Published artifact is SKILL.md alone.\n\nv1.0.0 | 2026-05-18T21:37:20.642Z | user\n\nv1.0.0 — First publication.\n\nInitial release. Ports the operating doctrine for TikTok brand-account\nautomation from a regulated-field operation to a domain-agnostic skill.\n\nIncludes:\n- 3-role mental model (tk-post / tk-engage / tk-stealth)\n- Phase A/B trust gradient with manual override path\n- Zero outgoing links in DMs and comments\n- The full human-like UI flow inside /business-suite/messages and\n  /business-suite/comments: physical cliclick, screen-coord computation,\n  the two-textbox trap (smallest vpY for nested reply), exit-code table,\n  the cliclick-t-accent encoding trap, \"what to do when UI changes\"\n- Strict reply qualification, hard quotas, cron interleave (3-min offset)\n- Anti-spam content + behavioral triggers\n- Hashtag + sound state management for shadowban auditing\n- Recovery playbook for soft blocks, automod removals, account warnings\n- 9-file memory inventory + first-run checklist + FAQ\n\nCompanion to reddit-account-operations + twitter-account-operations.\n\nArchive index:\n\nArchive v2.0.1: 3 files, 11120 bytes\n\nFiles: skill-card.md (2550b), SKILL.md (20356b), _meta.json (144b)\n\nFile v2.0.1:SKILL.md\n\n---\nname: tiktok-account-operations\ndescription: Run a TikTok Business account from cron — inbound DM/comment replies, quotas, phase gates, stop-on-block. Use when scheduling TikTok replies. Trigger on \"tiktok dm\", \"tiktok reply\".\nmetadata: {\"clawdbot\":{\"emoji\":\"🎵\",\"requires\":{\"bins\":[\"cliclick\",\"openclaw\"]},\"homepage\":\"https://clawhub.ai/skills/tiktok-account-operations\"}}\n---\n\n# TikTok Account Operations\n\n**The goal is not to reply fast. The goal is to operate one account you own with the care of a real contributor — under TikTok's Terms of Service and Community Guidelines, inside its published limits, and stopped the moment a platform control fires.**\n\n## Access this skill requires — read before anything else\n\nThis skill drives a **logged-in browser and your operating system's input layer**. That is a large amount of power: grant it deliberately, per item, and revoke it when the account is not being operated. Beyond `cliclick`, the snippets assume the OpenClaw browser CLI over CDP plus the macOS built-ins `pbcopy` and `osascript`.\n\n| Access | Why it is needed | If you decline |\n|---|---|---|\n| Logged-in TikTok browser profile (CDP on `127.0.0.1:<BROWSER_PORT>`) | Business Suite has no DM/comment-reply API; the UI is the only surface | Skill is unusable — no fallback |\n| macOS **Accessibility** permission on the cron's terminal (`cliclick`) | TikTok's composer reads React controlled state and ignores synthetic events; only real clicks/keystrokes reach it | Skill is unusable. **This permission lets that terminal click and type anywhere on your machine, not just in TikTok.** Use a dedicated low-privilege user session |\n| Clipboard (`pbcopy` + ⌘V) | Only reliable path for multi-line DM bodies | Use the typing path (§ Sending), ASCII only |\n| Screenshots / DOM snapshots to a scratch dir | Post-mortem when a reply lands in the wrong box | Skip — you lose debuggability, nothing else |\n| Alert webhook (Telegram/Slack/Discord) | Run recaps and blocker alerts | **Leave it unset — this is the default.** Recaps then stay local |\n\n**Network egress: off by default.** The only outbound call this skill makes beyond tiktok.com is the alert webhook, and only if you set `alerts.webhook`. Recaps sent there contain the account handle, counts, and lead usernames — do not point it at a channel wider than your operators.\n\n**Data persisted locally**, nowhere else — `<WORKSPACE_DIR>/memory/`, 9 files. Anything holding a third-party identifier is bounded; nothing here accumulates forever, and **you never store personal details a user sent you**.\n\n| File | Holds | Retention |\n|---|---|---|\n| `tiktok-recaps.md` · `tiktok-reply-log.md` | Run recaps · username + type + timestamp **only — never DM bodies** | **Prune at 90 days** |\n| `tiktok-alerts-sent.md` | A **hash of the username**, not the handle — dedupes lead alerts without keeping who | **Prune at 90 days** |\n| `tiktok-post-log.md` · `tiktok-state.md` · `tiktok-hashtag-state.md` · `tiktok-sound-state.md` · `tiktok-ideas.md` · `tiktok-learnings.md` | Your own posts, phase, warnings, follower count, per-hashtag/sound counters, content ideas, causes of removals | Counters and your own content only — no third-party identifiers |\n\n## When to use\n\n| You say | The skill does |\n|---|---|\n| \"check TikTok DMs\" | Session check → inbound reply pass, ≤4 per run (§ Sending) |\n| \"reply to new comments\" | Comment pass on `/business-suite/comments`, ≤6 per run |\n| \"my reply didn't post\" | § Troubleshooting — two-textbox trap first |\n| \"TikTok blocked my account\" / captcha appeared | **Stop. Hand to a human.** No retry, no workaround (§ Stop conditions) |\n| \"am I shadowbanned\" | Hashtag state check + Phase A flip |\n| \"DM this list of people\" | **Out of scope.** This skill replies; it does not prospect |\n\n## Configure\n\n| Placeholder | Example |\n|---|---|\n| `<BRAND_NAME>` / `<TIKTOK_HANDLE>` | \"Acme Studio\" / \"@acmestudio\" |\n| `<BROWSER_PROFILE>` / `<BROWSER_PORT>` | \"tiktok-live\" / \"18801\" |\n| `<PRIMARY_CTA>` | one channel only — bio link |\n| `<NICHE_KEYWORDS>` | \"lost license OR speeding ticket\" |\n| `<WORKSPACE_DIR>` | \"~/.openclaw/workspace/tiktok-acme\" |\n\n```yaml\n# <WORKSPACE_DIR>/config.yaml\ntiktok:    { handle: <TIKTOK_HANDLE>, business_account: true, browser_profile: <BROWSER_PROFILE>, browser_port: <BROWSER_PORT> }\ncta:       { primary: <PRIMARY_CTA> }\ndiscovery: { niche_keywords: <NICHE_KEYWORDS> }\nalerts:    { webhook: null }             # null = no network egress. Opt in explicitly.\nschedule:  { timezone: Europe/Paris,\n             dm_check:       \"0,15,30,45 9-22 * * *\",  # every 15 min\n             comment_check:  \"3,18,33,48 9-22 * * *\",  # +3 min — the two must never overlap\n             browser_health: \"0 * * * *\", daily_recap: \"0 21 * * *\" }  # recap carries the phase gate — do not drop it\n```\n\nAny CDP stack (Playwright, Puppeteer, Chrome MCP) works — swap the calls. Init the memory files:\n\n```bash\nmkdir -p \"<WORKSPACE_DIR>/memory\" && cd \"$_\" && for f in recaps post-log reply-log state hashtag-state sound-state ideas learnings alerts-sent; do [ -f \"tiktok-$f.md\" ] || touch \"tiktok-$f.md\"; done\n# Output: (silent, idempotent — existing files are never truncated)\n```\n\n## Roles — one profile, three intents\n\n| Role | Intent | Default page | Writes to TikTok? |\n|---|---|---|---|\n| `tk-post` | Act as the account: reply to inbound DMs and comments | `/business-suite/messages` | **Yes** |\n| `tk-engage` | Discover and qualify what deserves a reply | `/search?q=<NICHE_KEYWORDS>&t=video` | No |\n| `tk-observe` | **Read-only.** Look at the FYP, peer profiles, sounds. Nothing is sent, nothing is written, no account state changes | `/foryou` | No |\n\n`tk-observe` only reads — that is its definition, not a precaution. A workflow under it that wants to send, follow, or like is in the wrong role; move it to `tk-post` and spend its quota. After every run, return to the role's default page.\n\n## Session check — first call of every cron\n\n```bash\nopenclaw browser --browser-profile <BROWSER_PROFILE> status\n# Output: {\"state\":\"running\",\"port\":18801}\nopenclaw browser --browser-profile <BROWSER_PROFILE> evaluate --fn \"async () => { const r = await fetch('https://www.tiktok.com/api/user/detail/self/', {credentials:'include'}); const d = await r.json(); return {ok: d.statusCode === 0, uniqueId: d.userInfo?.user?.uniqueId || null}; }\"\n# Output: {\"ok\":true,\"uniqueId\":\"acmestudio\"}\n```\n\n`state: stopped`, `ok:false`, or `uniqueId:null` → report the blocker and exit. Never relaunch Chrome from inside a cron; that is a user-side action. Then navigate to `/business-suite/messages`: a redirect to the public feed or `/login` means the Business session is expired even when the API call succeeded.\n\n## Phase gate\n\n| Phase | Condition | Authorized |\n|---|---|---|\n| **A** | Account < 30 days **OR** a Community Guidelines notice in the last 14 days **OR** < 500 followers | Post videos; reply to **inbound** DMs only, no brand mention. Nothing else |\n| **B** | ≥ 30 days, no notice for 14 days, ≥ 500 followers | Full quotas below |\n\nThe gate has **no manual override** — the 30 days are how long TikTok takes to trust the account, and skipping them is the most reliable way to get it restricted. Read the phase from the last line of `tiktok-state.md`, confirm against `/business-suite/posts` (a \"limited\" banner near the post list = Phase A, immediately). The `daily_recap` cron detects A → B and **alerts and stops**: flipping the crons on is a human action. First week in B: cap at 2 replies/run.\n\n## Quotas — hard limits\n\n| Action | Limit |\n|---|---|\n| DM replies / 24 h · / run | 30 · 4 |\n| Comment replies / 24 h · / run | 40 · 6 |\n| **Outbound first-contact DMs** | **0.** This skill replies to people who wrote first — it never opens a thread. Unsolicited DM campaigns are out of scope and against TikTok's spam policy |\n| Follow / day | 20 (Phase B) · 5 (Phase A) — TikTok blocks the action for 24 h past ~25/day |\n| Unfollow / day | 20 |\n| Two actions in the same video thread | ≥ 30 min apart |\n| Any two actions | ≥ 1 min apart — bursts past ~6 actions/10 min hit a rate limit |\n\nRead `tiktok-reply-log.md` at the start of every run, count the last 24 h, and **abort early** when a quota is already met. The two platform thresholds above are quoted so the ceiling is verifiable, not so you can walk up to it: these quotas sit deliberately below them and the account never approaches them.\n\n## Qualification — every check must hold, or skip and say why in the recap\n\n| Check | Fails when | Action |\n|---|---|---|\n| Real message | It is a share, forward, or a language you cannot answer natively | Skip |\n| Genuine intent in your domain | A vent, a meme, a request for free expert work | Skip |\n| Not a repeat | A templated reply went to the same user < 7 days ago (`tiktok-reply-log.md`) | Skip |\n| Phase | Reply mentions the brand while in Phase A | Reply without the mention, or skip |\n| **Full context read first** | The whole DM thread (`[data-e2e=\"chat-item\"]`), or caption + comment thread, was not read | Read it, or skip — a reply that ignores obvious context is not a useful reply |\n\n## Reply content\n\n**Phase A**: 1–3 sentences, conversational, no brand, no link, no profession hint, never expert-grade advice (medical, legal, financial). **Phase B** — pedagogical, not promotional. DM ≤ 600 chars, comment ≤ 200:\n\n```\n[Acknowledge the situation in one neutral sentence.]\n[General framework, 2-3 short paragraphs. No statute quote. No price.]\n[One next step — <PRIMARY_CTA> only. Never list two channels.]\n```\n\nFilled instance (comment): `Six months is the usual window for that kind of request, not two weeks — the delay you're seeing is normal. Happy to point you in the right direction if you want to reach out.`\n\n**Zero outgoing links in DM or comment bodies** — the bio link is the only authorized URL surface (TikTok dampens reach on third-party URLs in user-visible text, captions included), and max one indirect brand mention per reply. Also forbidden: shorteners, phone numbers, emails, \"DM me\" / \"link in bio\", guaranteed outcomes, past client cases, payment solicitation, emojis (encoding trap — see § Troubleshooting), identical text across users (spam under TikTok's rules, and the fastest route to a temporary ban).\n\n**If asked \"are you a bot?\" — answer honestly and stop.** `\"Yes, replies on this account are assisted by automation, and a human on the <BRAND_NAME> team reads everything. What can I help with?\"` Then flag the thread for a human. Never claim to be a person, never give an evasive answer. Someone who asks is entitled to a straight answer, and misleading them protects nothing.\n\n## Posting cadence (outside the reply crons)\n\n3–5 short videos/week (15–45 s, one idea, hook in 1.5 s) + 1 longer (45–90 s). No burst (3+ in 6 h). 3–5 hashtags: 1 broad + 2–3 niche. **Sounds: original or commercially licensed only** — a copyrighted track on a Business account is a licensing breach and gets muted on the FYP. Update state at the end of every posting run:\n\n| File | One entry per | Fields |\n|---|---|---|\n| `tiktok-hashtag-state.md` | Hashtag | Last 5 videos that used it + reach (views, watch-time %) · banned/sensitive flag · manual override list |\n| `tiktok-sound-state.md` | Sound | Original or licensed · sound ID / link · reach with that sound vs your baseline |\n\n**Shadowban check** (this is how you notice a control already fired, per § Stop conditions — not how you get around one): search the hashtag in a private/incognito window. Your video does not appear → flip to Phase A and alert a human.\n\n## Sending — the UI mechanics\n\nTikTok's Business Suite composer reads from React controlled state and renders inside iframes. Synthetic events never reach the send pipeline, so real input is a **transport requirement, not a disguise**: it is how you reach a composer that ignores `dispatchEvent`. It does nothing to hide the automation, and must not be used to try. **What does NOT work** — each is the obvious first attempt, and each fails quietly:\n\n| Method | Why it fails |\n|---|---|\n| `cliclick kd:cmd t:v ku:cmd` | Types the literal letter \"v\" — the cmd keydown is released before `t:v` fires. Use `osascript` for ⌘V |\n| `cliclick kp:return` to send | The composer ignores the Enter key in DMs and comments. You must click the send button |\n| `execCommand('insertText')` | Visually fills the textbox, never reaches the send pipeline — the send button stays disabled |\n\n### DM send (validated)\n\nSession check → navigate `/messages?lang=<lang>`, wait ≥ 1.5 s for the redirect → `document.querySelectorAll('iframe').length > 0`, scope every query to that iframe → list unread `[data-e2e=\"chat-list-item\"]`, click the tile, read **all** `[data-e2e=\"chat-item\"]`, qualify, draft. Then:\n\n```\n1. pbcopy ← message on the clipboard, UTF-8       2. Page.bringToFront (CDP)\n3. screen coords of the textbox (see below)\n4. cliclick c:X,Y  ← physical click. Non-negotiable: without it the paste lands\n                     in whatever frame holds focus, rarely the textbox\n5. sleep 0.5    6. osascript -e 'tell app \"System Events\" to keystroke \"v\" using command down'\n7. sleep 1 ← the Send button enables asynchronously\n8. screen coords of [data-e2e=\"message-send\"] → cliclick c:X,Y    9. verify the textbox is empty\n```\n\n### Comment reply (nested)\n\nLeft pane = video list in the **main DOM**, badge `/(\\d+) (nouveau|new)/`. Right pane = **two iframes**: `iframe[0]` messages (background), `iframe[1]` comments — always scope to `iframe[1]`. Click the video tile → wait 2 s → read `[data-e2e=\"comment-level-1\"]` → qualify → click `[data-e2e=\"comment-reply-1\"]` → wait 1.5 s. **Two textboxes now exist.** The inline (nested) one has the *smallest* `vpY`; the large one at the bottom posts a new top-level comment instead.\n\n```python\ninline_tb = min(textboxes, key=lambda t: t[\"vpY\"])          # nested reply box\npost_btn  = min([b for b in post_btns if not b[\"disabled\"]],\n                key=lambda b: abs(b[\"vpY\"] - inline_tb[\"vpY\"]))\n```\n\nThen: `cliclick c:` on `inline_tb` → sleep 0.5 → `cliclick t:'ascii_only_message'` (**type, do not paste** — the ⌘V path targets page-level focus) → sleep 1 → `cliclick c:` on `post_btn`. Re-snapshot and confirm your username appears under the parent comment.\n\n### Viewport → screen coordinates\n\n```js\nconst m = { sX: window.screenX, sY: window.screenY, ih: window.innerHeight, oh: window.outerHeight };\n// screenX = sX + vpX\n// screenY = sY + (oh - ih) + vpY      // (oh - ih) = Chrome top bar, ≈ 87 on default macOS\n```\n\n### Exit codes\n\n| Code | Meaning | Recap |\n|---|---|---|\n| 0 | Sent and verified | `status: ok`, log the reply |\n| 1 | Fatal (ref missing, iframe absent, send never enabled) | `status: error` + screenshot |\n| 2 | Soft block / \"Tap to retry\" | `status: blocked`, stop, alert |\n| 3 | Session / login KO | `status: blocked`, alert to re-login |\n| 4 | Landed top-level instead of nested | `status: partial`, flag for manual cleanup |\n\n## Stop conditions — a platform control means hands off the keyboard\n\n**When TikTok challenges you, you stop. You do not adapt, retry, slow down, or route around it.** A control that fires is the platform telling you it does not consent to what is happening; the only correct response is a human looking at why. Anything else turns a doctrine that claims to protect the account into the thing that loses it.\n\n| Signal | Action |\n|---|---|\n| Captcha / puzzle challenge | **Stop the run. Never attempt to solve it, never re-present it, never hand it to a solver.** Alert a human, who completes it manually or decides not to |\n| HTTP 429 / rate limited | Stop. Wait for the next scheduled run. Do not shorten the interval, do not retry |\n| \"Tap to retry\" toast | Soft block. Pause the role ≥ 1 h. Report |\n| \"Action blocked\" / \"we'll let you know when you can do this again\" | Flip to Phase A. Disable reply crons. Manual review before re-enabling |\n| Banner: \"your account is at risk of restriction\" | Phase A immediately. All reply crons off until a human reviews |\n| Comment removed by TikTok < 10 min after publish | Auto-freeze the comment cron 6 h. Append the cause to `tiktok-learnings.md` |\n| Comment landed top-level | Flag for human cleanup. Pause comment cron 30 min |\n| Account banned or shadowbanned | Stop everything. **Do not auto-appeal** — automated appeals are themselves abuse. Human, manual, once |\n\n## Troubleshooting\n\n| Issue | Cause | Fix |\n|---|---|---|\n| Reply posted as a top-level comment | Two textboxes exist after clicking \"Reply\"; you took the general one | Take the smallest `vpY` (§ Sending), re-verify against the parent's children |\n| Accents vanished or turned to garbage in a sent comment | `cliclick t:'…'` silently drops/substitutes non-ASCII in some locales (`fr_FR.UTF-8`). **No error is raised** — the script looks correct, the reply looks illiterate | Strip accents and emoji before typing: `é→e`, `'→'`, `…→...` |\n| Paste landed somewhere else entirely | iframe focus drift — a JS click inside an iframe does not always focus the inner document | A physical `cliclick` after every navigation, before every paste or type |\n| `evaluate` returns null, no error | The tab is off tiktok.com | Navigate first, retry once |\n| Cron dies mid-send, no error | Default job timeout — a 120 s default kills a flow that was working | Posting crons need **≥ 1200 s** |\n| Selectors that worked yesterday return null | TikTok shipped a UI change | Snapshot manually, read the new `data-e2e` names, update the selector map, test against your own most recent post, then re-enable the cron |\n\n## Output format — mandatory recap, to `tiktok-recaps.md` and (only if set) the webhook\n\n```\n## YYYY-MM-DD HH:MM TZ — <job-id> — status: ok|partial|blocked|skipped\n- Phase: A|B\n- DMs sent: <N>   Comments sent: <N>\n- Hot leads: <list or \"—\">\n- Blockers: <text or \"—\">\n- Next useful action: <1 line>\n\n# ── filled instance ──\n## 2026-07-16 14:15 CEST — dm-check — status: blocked\n- Phase: B\n- DMs sent: 1   Comments sent: 0\n- Hot leads: @marie_l (asked about the 6-month window)\n- Blockers: captcha on 2nd conversation — run stopped, human needed\n- Next useful action: solve the challenge manually in the tiktok-live profile, then re-enable dm_check\n```\n\nNo action taken → say so and why. **Never fake a successful action.** Better silence than spam; better a blocker report than a false success.\n\n## First run\n\n- [ ] Placeholders filled; account is Business or Creator; bio = one line + a single `<PRIMARY_CTA>` link.\n- [ ] Browser profile up on `<BROWSER_PORT>`, session check returns a non-null `uniqueId`; `memory/` initialized (9 files).\n- [ ] `cliclick` installed; you have read § Access and accept what Accessibility grants that terminal.\n- [ ] `alerts.webhook` left null unless you want recaps leaving the machine.\n- [ ] Phase A confirmed; inbound-DM cron only.\n- [ ] **≥ 14 days of organic, hand-posted content before any reply cron** — even if the metrics say Phase B.\n\n## Scope\n\n**This skill ONLY:**\n- Operates **one account you own and control**, under TikTok's Terms of Service and Community Guidelines.\n- Replies to DMs and comments from people who contacted the account first.\n- Enforces quotas that sit deliberately below the platform's own limits.\n- Stops and alerts a human on any captcha, block, warning, or ban.\n- Persists local logs (usernames, counts, timestamps) and sends nothing off-machine unless you set a webhook.\n- Discloses the automation honestly to anyone who asks.\n\n**This skill NEVER:**\n- Bypasses, solves, or works around a captcha, a rate limit, a block, or any anti-abuse control — when one fires, the run ends and a human decides.\n- Tunes timing, dwell, or phrasing to look organic to a detector. Real input is used because the composer requires it, and for no other reason.\n- Sends unsolicited or first-contact DMs, bulk-identical messages, or engagement it did not earn.\n- Claims to be human, denies automation, or answers evasively when asked.\n- Operates an account you do not own, buys or borrows engagement, or automates appeals.\n- Runs unattended past a stop condition, or fakes a success it cannot verify.\n\nFile v2.0.1:_meta.json\n\n{\n  \"ownerId\": \"kn7f6shzecv3qv3wrr074s49f986ybe1\",\n  \"slug\": \"tiktok-account-operations\",\n  \"version\": \"2.0.1\",\n  \"publishedAt\": 1784214115555\n}\n\nFile v2.0.1:skill-card.md\n\n## Description:\n\nRun TikTok Business account operations from cron for inbound DM and comment replies, with quotas, phase gates, and stop-on-block controls.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[alexbloch-ia](https://clawhub.ai/user/alexbloch-ia)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nOperators and developers use this skill to schedule controlled TikTok Business account reply workflows for one account they own. It supports inbound DM and comment handling, local recaps, quota checks, stop conditions, and configuration guidance for browser-based operation.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: The skill requires terminal Accessibility control and a logged-in TikTok browser profile.\n\nMitigation: Use a dedicated low-privilege browser or user session, grant access deliberately, and revoke it when account operations are not running.\n\nRisk: Optional webhook alerts can send account handles, counts, and lead usernames outside the local machine.\n\nMitigation: Leave the webhook unset by default, and only enable it for channels limited to authorized operators.\n\nRisk: TikTok challenges, account warnings, or rate limits indicate that the platform has applied a control.\n\nMitigation: Stop the run, avoid retries or workarounds, alert a human operator, and resume only after manual review.\n\nRisk: Local operation logs include usernames, timestamps, and account activity summaries.\n\nMitigation: Keep logs in the configured workspace, avoid storing DM bodies or personal details, monitor local logs, and prune identifier-bearing logs on the documented retention schedule.\n\n## Reference(s):\n\n- [ClawHub skill page](https://clawhub.ai/alexbloch-ia/skills/tiktok-account-operations)\n- [ClawHub metadata homepage](https://clawhub.ai/skills/tiktok-account-operations)\n\n## Skill Output:\n\n**Output Type(s):** [Text, Markdown, Code, Shell commands, Configuration, Guidance]\n\n**Output Format:** [Markdown with YAML configuration and shell command snippets]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Produces local recap formats, operational checklists, quota guidance, and stop-condition handling instructions.]\n\n## Skill Version(s):\n\n2.0.1 (source: server release metadata)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment.\n\nArchive v2.0.0: 3 files, 11234 bytes\n\nFiles: skill-card.md (2910b), SKILL.md (20356b), _meta.json (144b)\n\nFile v2.0.0:SKILL.md\n\n---\nname: tiktok-account-operations\ndescription: Run a TikTok Business account from cron — inbound DM/comment replies, quotas, phase gates, stop-on-block. Use when scheduling TikTok replies. Trigger on \"tiktok dm\", \"tiktok reply\".\nmetadata: {\"clawdbot\":{\"emoji\":\"🎵\",\"requires\":{\"bins\":[\"cliclick\",\"openclaw\"]},\"homepage\":\"https://clawhub.ai/skills/tiktok-account-operations\"}}\n---\n\n# TikTok Account Operations\n\n**The goal is not to reply fast. The goal is to operate one account you own with the care of a real contributor — under TikTok's Terms of Service and Community Guidelines, inside its published limits, and stopped the moment a platform control fires.**\n\n## Access this skill requires — read before anything else\n\nThis skill drives a **logged-in browser and your operating system's input layer**. That is a large amount of power: grant it deliberately, per item, and revoke it when the account is not being operated. Beyond `cliclick`, the snippets assume the OpenClaw browser CLI over CDP plus the macOS built-ins `pbcopy` and `osascript`.\n\n| Access | Why it is needed | If you decline |\n|---|---|---|\n| Logged-in TikTok browser profile (CDP on `127.0.0.1:<BROWSER_PORT>`) | Business Suite has no DM/comment-reply API; the UI is the only surface | Skill is unusable — no fallback |\n| macOS **Accessibility** permission on the cron's terminal (`cliclick`) | TikTok's composer reads React controlled state and ignores synthetic events; only real clicks/keystrokes reach it | Skill is unusable. **This permission lets that terminal click and type anywhere on your machine, not just in TikTok.** Use a dedicated low-privilege user session |\n| Clipboard (`pbcopy` + ⌘V) | Only reliable path for multi-line DM bodies | Use the typing path (§ Sending), ASCII only |\n| Screenshots / DOM snapshots to a scratch dir | Post-mortem when a reply lands in the wrong box | Skip — you lose debuggability, nothing else |\n| Alert webhook (Telegram/Slack/Discord) | Run recaps and blocker alerts | **Leave it unset — this is the default.** Recaps then stay local |\n\n**Network egress: off by default.** The only outbound call this skill makes beyond tiktok.com is the alert webhook, and only if you set `alerts.webhook`. Recaps sent there contain the account handle, counts, and lead usernames — do not point it at a channel wider than your operators.\n\n**Data persisted locally**, nowhere else — `<WORKSPACE_DIR>/memory/`, 9 files. Anything holding a third-party identifier is bounded; nothing here accumulates forever, and **you never store personal details a user sent you**.\n\n| File | Holds | Retention |\n|---|---|---|\n| `tiktok-recaps.md` · `tiktok-reply-log.md` | Run recaps · username + type + timestamp **only — never DM bodies** | **Prune at 90 days** |\n| `tiktok-alerts-sent.md` | A **hash of the username**, not the handle — dedupes lead alerts without keeping who | **Prune at 90 days** |\n| `tiktok-post-log.md` · `tiktok-state.md` · `tiktok-hashtag-state.md` · `tiktok-sound-state.md` · `tiktok-ideas.md` · `tiktok-learnings.md` | Your own posts, phase, warnings, follower count, per-hashtag/sound counters, content ideas, causes of removals | Counters and your own content only — no third-party identifiers |\n\n## When to use\n\n| You say | The skill does |\n|---|---|\n| \"check TikTok DMs\" | Session check → inbound reply pass, ≤4 per run (§ Sending) |\n| \"reply to new comments\" | Comment pass on `/business-suite/comments`, ≤6 per run |\n| \"my reply didn't post\" | § Troubleshooting — two-textbox trap first |\n| \"TikTok blocked my account\" / captcha appeared | **Stop. Hand to a human.** No retry, no workaround (§ Stop conditions) |\n| \"am I shadowbanned\" | Hashtag state check + Phase A flip |\n| \"DM this list of people\" | **Out of scope.** This skill replies; it does not prospect |\n\n## Configure\n\n| Placeholder | Example |\n|---|---|\n| `<BRAND_NAME>` / `<TIKTOK_HANDLE>` | \"Acme Studio\" / \"@acmestudio\" |\n| `<BROWSER_PROFILE>` / `<BROWSER_PORT>` | \"tiktok-live\" / \"18801\" |\n| `<PRIMARY_CTA>` | one channel only — bio link |\n| `<NICHE_KEYWORDS>` | \"lost license OR speeding ticket\" |\n| `<WORKSPACE_DIR>` | \"~/.openclaw/workspace/tiktok-acme\" |\n\n```yaml\n# <WORKSPACE_DIR>/config.yaml\ntiktok:    { handle: <TIKTOK_HANDLE>, business_account: true, browser_profile: <BROWSER_PROFILE>, browser_port: <BROWSER_PORT> }\ncta:       { primary: <PRIMARY_CTA> }\ndiscovery: { niche_keywords: <NICHE_KEYWORDS> }\nalerts:    { webhook: null }             # null = no network egress. Opt in explicitly.\nschedule:  { timezone: Europe/Paris,\n             dm_check:       \"0,15,30,45 9-22 * * *\",  # every 15 min\n             comment_check:  \"3,18,33,48 9-22 * * *\",  # +3 min — the two must never overlap\n             browser_health: \"0 * * * *\", daily_recap: \"0 21 * * *\" }  # recap carries the phase gate — do not drop it\n```\n\nAny CDP stack (Playwright, Puppeteer, Chrome MCP) works — swap the calls. Init the memory files:\n\n```bash\nmkdir -p \"<WORKSPACE_DIR>/memory\" && cd \"$_\" && for f in recaps post-log reply-log state hashtag-state sound-state ideas learnings alerts-sent; do [ -f \"tiktok-$f.md\" ] || touch \"tiktok-$f.md\"; done\n# Output: (silent, idempotent — existing files are never truncated)\n```\n\n## Roles — one profile, three intents\n\n| Role | Intent | Default page | Writes to TikTok? |\n|---|---|---|---|\n| `tk-post` | Act as the account: reply to inbound DMs and comments | `/business-suite/messages` | **Yes** |\n| `tk-engage` | Discover and qualify what deserves a reply | `/search?q=<NICHE_KEYWORDS>&t=video` | No |\n| `tk-observe` | **Read-only.** Look at the FYP, peer profiles, sounds. Nothing is sent, nothing is written, no account state changes | `/foryou` | No |\n\n`tk-observe` only reads — that is its definition, not a precaution. A workflow under it that wants to send, follow, or like is in the wrong role; move it to `tk-post` and spend its quota. After every run, return to the role's default page.\n\n## Session check — first call of every cron\n\n```bash\nopenclaw browser --browser-profile <BROWSER_PROFILE> status\n# Output: {\"state\":\"running\",\"port\":18801}\nopenclaw browser --browser-profile <BROWSER_PROFILE> evaluate --fn \"async () => { const r = await fetch('https://www.tiktok.com/api/user/detail/self/', {credentials:'include'}); const d = await r.json(); return {ok: d.statusCode === 0, uniqueId: d.userInfo?.user?.uniqueId || null}; }\"\n# Output: {\"ok\":true,\"uniqueId\":\"acmestudio\"}\n```\n\n`state: stopped`, `ok:false`, or `uniqueId:null` → report the blocker and exit. Never relaunch Chrome from inside a cron; that is a user-side action. Then navigate to `/business-suite/messages`: a redirect to the public feed or `/login` means the Business session is expired even when the API call succeeded.\n\n## Phase gate\n\n| Phase | Condition | Authorized |\n|---|---|---|\n| **A** | Account < 30 days **OR** a Community Guidelines notice in the last 14 days **OR** < 500 followers | Post videos; reply to **inbound** DMs only, no brand mention. Nothing else |\n| **B** | ≥ 30 days, no notice for 14 days, ≥ 500 followers | Full quotas below |\n\nThe gate has **no manual override** — the 30 days are how long TikTok takes to trust the account, and skipping them is the most reliable way to get it restricted. Read the phase from the last line of `tiktok-state.md`, confirm against `/business-suite/posts` (a \"limited\" banner near the post list = Phase A, immediately). The `daily_recap` cron detects A → B and **alerts and stops**: flipping the crons on is a human action. First week in B: cap at 2 replies/run.\n\n## Quotas — hard limits\n\n| Action | Limit |\n|---|---|\n| DM replies / 24 h · / run | 30 · 4 |\n| Comment replies / 24 h · / run | 40 · 6 |\n| **Outbound first-contact DMs** | **0.** This skill replies to people who wrote first — it never opens a thread. Unsolicited DM campaigns are out of scope and against TikTok's spam policy |\n| Follow / day | 20 (Phase B) · 5 (Phase A) — TikTok blocks the action for 24 h past ~25/day |\n| Unfollow / day | 20 |\n| Two actions in the same video thread | ≥ 30 min apart |\n| Any two actions | ≥ 1 min apart — bursts past ~6 actions/10 min hit a rate limit |\n\nRead `tiktok-reply-log.md` at the start of every run, count the last 24 h, and **abort early** when a quota is already met. The two platform thresholds above are quoted so the ceiling is verifiable, not so you can walk up to it: these quotas sit deliberately below them and the account never approaches them.\n\n## Qualification — every check must hold, or skip and say why in the recap\n\n| Check | Fails when | Action |\n|---|---|---|\n| Real message | It is a share, forward, or a language you cannot answer natively | Skip |\n| Genuine intent in your domain | A vent, a meme, a request for free expert work | Skip |\n| Not a repeat | A templated reply went to the same user < 7 days ago (`tiktok-reply-log.md`) | Skip |\n| Phase | Reply mentions the brand while in Phase A | Reply without the mention, or skip |\n| **Full context read first** | The whole DM thread (`[data-e2e=\"chat-item\"]`), or caption + comment thread, was not read | Read it, or skip — a reply that ignores obvious context is not a useful reply |\n\n## Reply content\n\n**Phase A**: 1–3 sentences, conversational, no brand, no link, no profession hint, never expert-grade advice (medical, legal, financial). **Phase B** — pedagogical, not promotional. DM ≤ 600 chars, comment ≤ 200:\n\n```\n[Acknowledge the situation in one neutral sentence.]\n[General framework, 2-3 short paragraphs. No statute quote. No price.]\n[One next step — <PRIMARY_CTA> only. Never list two channels.]\n```\n\nFilled instance (comment): `Six months is the usual window for that kind of request, not two weeks — the delay you're seeing is normal. Happy to point you in the right direction if you want to reach out.`\n\n**Zero outgoing links in DM or comment bodies** — the bio link is the only authorized URL surface (TikTok dampens reach on third-party URLs in user-visible text, captions included), and max one indirect brand mention per reply. Also forbidden: shorteners, phone numbers, emails, \"DM me\" / \"link in bio\", guaranteed outcomes, past client cases, payment solicitation, emojis (encoding trap — see § Troubleshooting), identical text across users (spam under TikTok's rules, and the fastest route to a temporary ban).\n\n**If asked \"are you a bot?\" — answer honestly and stop.** `\"Yes, replies on this account are assisted by automation, and a human on the <BRAND_NAME> team reads everything. What can I help with?\"` Then flag the thread for a human. Never claim to be a person, never give an evasive answer. Someone who asks is entitled to a straight answer, and misleading them protects nothing.\n\n## Posting cadence (outside the reply crons)\n\n3–5 short videos/week (15–45 s, one idea, hook in 1.5 s) + 1 longer (45–90 s). No burst (3+ in 6 h). 3–5 hashtags: 1 broad + 2–3 niche. **Sounds: original or commercially licensed only** — a copyrighted track on a Business account is a licensing breach and gets muted on the FYP. Update state at the end of every posting run:\n\n| File | One entry per | Fields |\n|---|---|---|\n| `tiktok-hashtag-state.md` | Hashtag | Last 5 videos that used it + reach (views, watch-time %) · banned/sensitive flag · manual override list |\n| `tiktok-sound-state.md` | Sound | Original or licensed · sound ID / link · reach with that sound vs your baseline |\n\n**Shadowban check** (this is how you notice a control already fired, per § Stop conditions — not how you get around one): search the hashtag in a private/incognito window. Your video does not appear → flip to Phase A and alert a human.\n\n## Sending — the UI mechanics\n\nTikTok's Business Suite composer reads from React controlled state and renders inside iframes. Synthetic events never reach the send pipeline, so real input is a **transport requirement, not a disguise**: it is how you reach a composer that ignores `dispatchEvent`. It does nothing to hide the automation, and must not be used to try. **What does NOT work** — each is the obvious first attempt, and each fails quietly:\n\n| Method | Why it fails |\n|---|---|\n| `cliclick kd:cmd t:v ku:cmd` | Types the literal letter \"v\" — the cmd keydown is released before `t:v` fires. Use `osascript` for ⌘V |\n| `cliclick kp:return` to send | The composer ignores the Enter key in DMs and comments. You must click the send button |\n| `execCommand('insertText')` | Visually fills the textbox, never reaches the send pipeline — the send button stays disabled |\n\n### DM send (validated)\n\nSession check → navigate `/messages?lang=<lang>`, wait ≥ 1.5 s for the redirect → `document.querySelectorAll('iframe').length > 0`, scope every query to that iframe → list unread `[data-e2e=\"chat-list-item\"]`, click the tile, read **all** `[data-e2e=\"chat-item\"]`, qualify, draft. Then:\n\n```\n1. pbcopy ← message on the clipboard, UTF-8       2. Page.bringToFront (CDP)\n3. screen coords of the textbox (see below)\n4. cliclick c:X,Y  ← physical click. Non-negotiable: without it the paste lands\n                     in whatever frame holds focus, rarely the textbox\n5. sleep 0.5    6. osascript -e 'tell app \"System Events\" to keystroke \"v\" using command down'\n7. sleep 1 ← the Send button enables asynchronously\n8. screen coords of [data-e2e=\"message-send\"] → cliclick c:X,Y    9. verify the textbox is empty\n```\n\n### Comment reply (nested)\n\nLeft pane = video list in the **main DOM**, badge `/(\\d+) (nouveau|new)/`. Right pane = **two iframes**: `iframe[0]` messages (background), `iframe[1]` comments — always scope to `iframe[1]`. Click the video tile → wait 2 s → read `[data-e2e=\"comment-level-1\"]` → qualify → click `[data-e2e=\"comment-reply-1\"]` → wait 1.5 s. **Two textboxes now exist.** The inline (nested) one has the *smallest* `vpY`; the large one at the bottom posts a new top-level comment instead.\n\n```python\ninline_tb = min(textboxes, key=lambda t: t[\"vpY\"])          # nested reply box\npost_btn  = min([b for b in post_btns if not b[\"disabled\"]],\n                key=lambda b: abs(b[\"vpY\"] - inline_tb[\"vpY\"]))\n```\n\nThen: `cliclick c:` on `inline_tb` → sleep 0.5 → `cliclick t:'ascii_only_message'` (**type, do not paste** — the ⌘V path targets page-level focus) → sleep 1 → `cliclick c:` on `post_btn`. Re-snapshot and confirm your username appears under the parent comment.\n\n### Viewport → screen coordinates\n\n```js\nconst m = { sX: window.screenX, sY: window.screenY, ih: window.innerHeight, oh: window.outerHeight };\n// screenX = sX + vpX\n// screenY = sY + (oh - ih) + vpY      // (oh - ih) = Chrome top bar, ≈ 87 on default macOS\n```\n\n### Exit codes\n\n| Code | Meaning | Recap |\n|---|---|---|\n| 0 | Sent and verified | `status: ok`, log the reply |\n| 1 | Fatal (ref missing, iframe absent, send never enabled) | `status: error` + screenshot |\n| 2 | Soft block / \"Tap to retry\" | `status: blocked`, stop, alert |\n| 3 | Session / login KO | `status: blocked`, alert to re-login |\n| 4 | Landed top-level instead of nested | `status: partial`, flag for manual cleanup |\n\n## Stop conditions — a platform control means hands off the keyboard\n\n**When TikTok challenges you, you stop. You do not adapt, retry, slow down, or route around it.** A control that fires is the platform telling you it does not consent to what is happening; the only correct response is a human looking at why. Anything else turns a doctrine that claims to protect the account into the thing that loses it.\n\n| Signal | Action |\n|---|---|\n| Captcha / puzzle challenge | **Stop the run. Never attempt to solve it, never re-present it, never hand it to a solver.** Alert a human, who completes it manually or decides not to |\n| HTTP 429 / rate limited | Stop. Wait for the next scheduled run. Do not shorten the interval, do not retry |\n| \"Tap to retry\" toast | Soft block. Pause the role ≥ 1 h. Report |\n| \"Action blocked\" / \"we'll let you know when you can do this again\" | Flip to Phase A. Disable reply crons. Manual review before re-enabling |\n| Banner: \"your account is at risk of restriction\" | Phase A immediately. All reply crons off until a human reviews |\n| Comment removed by TikTok < 10 min after publish | Auto-freeze the comment cron 6 h. Append the cause to `tiktok-learnings.md` |\n| Comment landed top-level | Flag for human cleanup. Pause comment cron 30 min |\n| Account banned or shadowbanned | Stop everything. **Do not auto-appeal** — automated appeals are themselves abuse. Human, manual, once |\n\n## Troubleshooting\n\n| Issue | Cause | Fix |\n|---|---|---|\n| Reply posted as a top-level comment | Two textboxes exist after clicking \"Reply\"; you took the general one | Take the smallest `vpY` (§ Sending), re-verify against the parent's children |\n| Accents vanished or turned to garbage in a sent comment | `cliclick t:'…'` silently drops/substitutes non-ASCII in some locales (`fr_FR.UTF-8`). **No error is raised** — the script looks correct, the reply looks illiterate | Strip accents and emoji before typing: `é→e`, `'→'`, `…→...` |\n| Paste landed somewhere else entirely | iframe focus drift — a JS click inside an iframe does not always focus the inner document | A physical `cliclick` after every navigation, before every paste or type |\n| `evaluate` returns null, no error | The tab is off tiktok.com | Navigate first, retry once |\n| Cron dies mid-send, no error | Default job timeout — a 120 s default kills a flow that was working | Posting crons need **≥ 1200 s** |\n| Selectors that worked yesterday return null | TikTok shipped a UI change | Snapshot manually, read the new `data-e2e` names, update the selector map, test against your own most recent post, then re-enable the cron |\n\n## Output format — mandatory recap, to `tiktok-recaps.md` and (only if set) the webhook\n\n```\n## YYYY-MM-DD HH:MM TZ — <job-id> — status: ok|partial|blocked|skipped\n- Phase: A|B\n- DMs sent: <N>   Comments sent: <N>\n- Hot leads: <list or \"—\">\n- Blockers: <text or \"—\">\n- Next useful action: <1 line>\n\n# ── filled instance ──\n## 2026-07-16 14:15 CEST — dm-check — status: blocked\n- Phase: B\n- DMs sent: 1   Comments sent: 0\n- Hot leads: @marie_l (asked about the 6-month window)\n- Blockers: captcha on 2nd conversation — run stopped, human needed\n- Next useful action: solve the challenge manually in the tiktok-live profile, then re-enable dm_check\n```\n\nNo action taken → say so and why. **Never fake a successful action.** Better silence than spam; better a blocker report than a false success.\n\n## First run\n\n- [ ] Placeholders filled; account is Business or Creator; bio = one line + a single `<PRIMARY_CTA>` link.\n- [ ] Browser profile up on `<BROWSER_PORT>`, session check returns a non-null `uniqueId`; `memory/` initialized (9 files).\n- [ ] `cliclick` installed; you have read § Access and accept what Accessibility grants that terminal.\n- [ ] `alerts.webhook` left null unless you want recaps leaving the machine.\n- [ ] Phase A confirmed; inbound-DM cron only.\n- [ ] **≥ 14 days of organic, hand-posted content before any reply cron** — even if the metrics say Phase B.\n\n## Scope\n\n**This skill ONLY:**\n- Operates **one account you own and control**, under TikTok's Terms of Service and Community Guidelines.\n- Replies to DMs and comments from people who contacted the account first.\n- Enforces quotas that sit deliberately below the platform's own limits.\n- Stops and alerts a human on any captcha, block, warning, or ban.\n- Persists local logs (usernames, counts, timestamps) and sends nothing off-machine unless you set a webhook.\n- Discloses the automation honestly to anyone who asks.\n\n**This skill NEVER:**\n- Bypasses, solves, or works around a captcha, a rate limit, a block, or any anti-abuse control — when one fires, the run ends and a human decides.\n- Tunes timing, dwell, or phrasing to look organic to a detector. Real input is used because the composer requires it, and for no other reason.\n- Sends unsolicited or first-contact DMs, bulk-identical messages, or engagement it did not earn.\n- Claims to be human, denies automation, or answers evasively when asked.\n- Operates an account you do not own, buys or borrows engagement, or automates appeals.\n- Runs unattended past a stop condition, or fakes a success it cannot verify.\n\nFile v2.0.0:_meta.json\n\n{\n  \"ownerId\": \"kn7f6shzecv3qv3wrr074s49f986ybe1\",\n  \"slug\": \"tiktok-account-operations\",\n  \"version\": \"2.0.0\",\n  \"publishedAt\": 1784196420640\n}\n\nFile v2.0.0:skill-card.md\n\n## Description: <br>\nRun a TikTok Business account from cron for inbound DM and comment replies with quotas, phase gates, and stop-on-block behavior. <br>\n\nThis skill is ready for commercial/non-commercial use. <br>\n\n## Publisher: <br>\n[alexbloch-ia](https://clawhub.ai/user/alexbloch-ia) <br>\n\n### License/Terms of Use: <br>\nMIT-0 <br>\n\n\n## Use Case: <br>\nExternal account operators and developers use this skill to schedule and execute TikTok Business inbound DM and comment reply runs for an account they own. It focuses on quotas, phase gates, local recaps, and human stop conditions when TikTok presents a challenge, block, or account-risk warning. <br>\n\n### Deployment Geography for Use: <br>\nGlobal <br>\n\n## Known Risks and Mitigations: <br>\nRisk: The skill requires control over a logged-in TikTok browser and macOS Accessibility input, which can affect more than TikTok if granted broadly. <br>\nMitigation: Use a dedicated low-privilege session, grant permissions deliberately, revoke them when not operating the account, and review runs before enabling automation. <br>\nRisk: Continuing after a captcha, rate limit, block, or account-risk warning can violate platform controls or damage the account. <br>\nMitigation: Stop immediately on platform controls, do not solve or route around challenges, and require human review before resuming. <br>\nRisk: Local recaps and optional webhooks may expose account handles, counts, and lead usernames. <br>\nMitigation: Keep the webhook unset unless operators need it, restrict any webhook channel, avoid storing DM bodies, and prune local identifier logs after 90 days. <br>\nRisk: Automated replies can become spammy, out of context, or non-compliant if used for outreach instead of inbound responses. <br>\nMitigation: Reply only to inbound DMs and comments, enforce quotas and phase gates, read full context before responding, and never send first-contact DMs or bulk-identical messages. <br>\n\n\n## Reference(s): <br>\n- [ClawHub skill page](https://clawhub.ai/alexbloch-ia/skills/tiktok-account-operations) <br>\n- [ClawHub metadata homepage](https://clawhub.ai/skills/tiktok-account-operations) <br>\n\n\n## Skill Output: <br>\n**Output Type(s):** [Markdown, Shell commands, Configuration, Guidance] <br>\n**Output Format:** [Markdown with YAML configuration examples, shell commands, browser automation steps, and a run recap template] <br>\n**Output Parameters:** [1D] <br>\n**Other Properties Related to Output:** [Guides local recap and log files, with optional webhook recaps only when explicitly configured.] <br>\n\n## Skill Version(s): <br>\n2.0.0 (source: server release metadata) <br>\n\n## Ethical Considerations: <br>\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment. <br>\n\nArchive v1.0.0: 4 files, 18343 bytes\n\nFiles: README.md (5319b), skill-card.md (2923b), SKILL.md (32572b), _meta.json (144b)\n\nFile v1.0.0:SKILL.md\n\n---\nname: tiktok-account-operations\ndescription: Operating doctrine for TikTok account automation — careful, value-adding creator pattern with role separation, business-suite based DM/comment ops, human-like UI flow (cliclick + nested reply textbox), strict anti-shadowban quotas, and recovery patterns. Use this for any scheduled TikTok activity (cron, agent, recurring task) where account safety, FYP eligibility and long-term reach matter more than raw output.\n---\n\n# TikTok Account Operations\n\nThis skill is the operating doctrine for every TikTok automation run on a brand, professional or personal account.\n\n**The goal is not to comment fast. The goal is to operate TikTok like a careful human contributor: stable browser, correct context, useful action, no shadowban risk, no FYP throttling.**\n\nDrop-in for any niche (legal, medical, software, finance, creator, ecommerce). Replace the placeholders in section 0 with your own values.\n\n---\n\n## 0. Configure for your brand\n\nBefore running anything, fill these placeholders in your local copy or your agent's memory:\n\n| Placeholder | Example | Your value |\n|---|---|---|\n| `<BRAND_NAME>` | \"Acme Studio\" | — |\n| `<BRAND_DOMAIN>` | \"acme.studio\" | — |\n| `<TIKTOK_HANDLE>` | \"@acmestudio\" | — |\n| `<TIKTOK_BUSINESS_ACCOUNT>` | yes/no (Business Suite requires Business or Creator account) | — |\n| `<BROWSER_PROFILE>` | \"tiktok-live\" | — |\n| `<BROWSER_PORT>` | \"18801\" | — |\n| `<NICHE_KEYWORDS>` | \"lost license OR speeding ticket\" | — |\n| `<PRIMARY_CTA>` | \"WhatsApp / form / app link — pick ONE\" | — |\n| `<WORKSPACE_DIR>` | \"~/.openclaw/workspace/tiktok-<brand>\" | — |\n\nAll shell snippets below assume an [OpenClaw](https://openclaw.ai) browser CLI bound by CDP, but the doctrine works with any browser-automation stack (Playwright, Puppeteer, Chrome MCP). Swap the CLI calls for your own.\n\n### Quick config (copy-paste YAML)\n\nIf your agent reads config from YAML, drop this in `<WORKSPACE_DIR>/config.yaml`:\n\n```yaml\nbrand:\n  name: <BRAND_NAME>\n  domain: <BRAND_DOMAIN>\n\ntiktok:\n  handle: <TIKTOK_HANDLE>            # e.g. \"@acmestudio\"\n  business_account: true             # required for /business-suite/ UI\n  browser_profile: <BROWSER_PROFILE> # e.g. \"tiktok-live\"\n  browser_port: <BROWSER_PORT>       # e.g. 18801\n\ndiscovery:\n  niche_keywords: <NICHE_KEYWORDS>\n\ncta:\n  primary: <PRIMARY_CTA>             # one channel only — e.g. \"wa.me/<digits>\"\n\nworkspace:\n  dir: <WORKSPACE_DIR>               # e.g. \"~/.openclaw/workspace/tiktok-acme\"\n\nalerts:\n  channel: telegram | slack | discord\n  webhook: <YOUR_WEBHOOK_URL>\n\nschedule:\n  timezone: Europe/Paris             # everything else is relative to this\n  windows:\n    dm_check:      \"0,15,30,45 9-22 * * *\"     # every 15 min, 9am-10pm\n    comment_check: \"3,18,33,48 9-22 * * *\"     # offset by 3 min so they never overlap\n    browser_health: \"0 * * * *\"                # hourly\n    daily_recap:   \"21:00\"\n```\n\n### Compatibility\n\nThe skill is markdown — it works wherever an agent reads `SKILL.md`:\n\n| Stack | Skill install path |\n|---|---|\n| [Claude Code](https://claude.ai/code) | `~/.claude/skills/tiktok-account-operations/` |\n| [OpenClaw](https://openclaw.ai) | `~/.openclaw/skills/tiktok-account-operations/` |\n| ClawHub-published | one-click install via [clawhub.ai](https://clawhub.ai) |\n| Cursor / Copilot CLI | drop `SKILL.md` into your project's `.cursorrules` or `AGENTS.md` |\n| Any LLM agent reading markdown rules | concatenate `SKILL.md` into your system prompt |\n\n---\n\n## 1. Browser architecture\n\n### Physical profile\n\nA single browser profile per account, attached to TikTok's Business Suite:\n\n- `<BROWSER_PROFILE>` (CDP direct, attached to `http://127.0.0.1:<BROWSER_PORT>`)\n\nUser data dir: `~/.openclaw-browser-profiles/<BROWSER_PROFILE>`.\n\nUse it through the OpenClaw browser CLI:\n\n```bash\nopenclaw browser --browser-profile <BROWSER_PROFILE> status\nopenclaw browser --browser-profile <BROWSER_PROFILE> tabs\nopenclaw browser --browser-profile <BROWSER_PROFILE> navigate https://www.tiktok.com/business-suite/messages\nopenclaw browser --browser-profile <BROWSER_PROFILE> snapshot --limit 160\nopenclaw browser --browser-profile <BROWSER_PROFILE> click <ref>\nopenclaw browser --browser-profile <BROWSER_PROFILE> evaluate --fn \"<async function>\"\n```\n\n### The Business Suite is the canonical surface\n\nMost TikTok operational pages (Messages, Comments, Analytics, Posts) live under:\n\n- `https://www.tiktok.com/business-suite/messages`\n- `https://www.tiktok.com/business-suite/comments`\n- `https://www.tiktok.com/business-suite/analytics`\n- `https://www.tiktok.com/business-suite/posts`\n\nCritical UI fact: **`/business-suite/messages` and `/business-suite/comments` render their content inside iframes**, not in the main DOM. Many `document.querySelector(...)` calls will silently miss the actual content unless you traverse the right iframe. See §9.\n\n### Roles (mental separation, single physical profile)\n\n#### Role: `tk-post`\n\nAccount cockpit. Use for direct account action.\n\nUse for:\n- DM replies in `/business-suite/messages`\n- Comment replies in `/business-suite/comments` (nested reply only — see §9)\n- Original video uploads (separate publishing flow, usually via mobile or Creator Studio)\n- Profile checks\n- Analytics snapshots\n\nDefault page: `https://www.tiktok.com/business-suite/messages`\n\nMental model: act as the account. Protect it. Do not clutter it.\n\n#### Role: `tk-engage`\n\nSearch and discovery radar. Use to find what deserves action.\n\nUse for:\n- Keyword searches across the public feed\n- Monitoring `#niche` hashtags\n- Inspecting a candidate creator's recent posts before replying\n- Trend / sound discovery\n\nDefault page: `https://www.tiktok.com/search?q=<NICHE_KEYWORDS_URL_ENCODED>&t=video`\n\nMental model: discover, qualify, decide. Do not act impulsively from discovery.\n\n#### Role: `tk-stealth`\n\nQuiet maintenance bay. Use for low-noise maintenance.\n\nUse for:\n- Saving / liking videos for inspiration\n- Following peer creators (cap follows — see §7)\n- Quiet profile observation\n- Sound library curation\n\nDefault page: `https://www.tiktok.com/foryou`\n\nMental model: maintain quietly. No noisy engagement.\n\n### Operational law\n\n- `tk-post` = act as the account\n- `tk-engage` = discover what to react to\n- `tk-stealth` = maintain quietly\n- Stability matters more than speed\n- After every run, navigate back to the role's default page (resting state)\n\nNever collapse all workflows into chaotic browsing.\n\n---\n\n## 2. Session check (run first on every cron)\n\n```bash\nopenclaw browser --browser-profile <BROWSER_PROFILE> status\nopenclaw browser --browser-profile <BROWSER_PROFILE> evaluate --fn \"async () => { try { const r = await fetch('https://www.tiktok.com/api/user/detail/self/', {credentials:'include'}); const d = await r.json(); return {ok: d.statusCode === 0, uniqueId: d.userInfo?.user?.uniqueId || null}; } catch(e) { return {ok:false, error:String(e)}; } }\"\n```\n\n- If `status` is `stopped`: report the blockage in your alert channel and stop. Never relaunch Chrome from inside a cron — that's a user-side action.\n- If `ok` is `false` or `uniqueId` is `null`: login expired. Stop and report.\n\nFor Business Suite specifically, navigate to `/business-suite/messages` after the API check — if the page redirects to the public web feed or to a login screen, the Business account session is expired even though the API succeeded.\n\n---\n\n## 3. Phase gating (algorithm trust)\n\nTikTok has no karma counter, but it has a strong **trust gradient**: brand-new or recently-warned accounts get throttled on the FYP and on DM volume. Run in two phases:\n\n- **Phase A** (account < 30 days OR last 14 days had ≥ 1 \"Community Guidelines\" notice): zero brand-mentioning DMs and zero outbound comment replies. The account is allowed to post videos and react to incoming DMs only.\n- **Phase B** (account ≥ 30 days, no recent warning, ≥ 500 followers): all crons authorized.\n\nAlways read `<WORKSPACE_DIR>/memory/tiktok-state.md` at start (last line = current phase) AND verify via `/business-suite/posts` (looks for a \"limited\" banner near the post list).\n\nA Daily Metrics Recap cron appends a snapshot every night and pings your alert channel explicitly when the account crosses Phase A → Phase B.\n\n### Manual override (advanced)\n\nYou can force Phase B before the 30-day threshold by appending a line `YYYY-MM-DD - phase=B (manual override)` to `tiktok-state.md`. The override line takes priority over the threshold. Use when:\n\n- You have an established brand on other platforms and an audited account history (imported followers, verified domain) and want immediate brand-aware posting.\n- You accept the risk of harder shadowban triggers in the first 30 days. The auto-freeze on 2 successive comment removals (see §11) still applies.\n\nDocument the decision in `tiktok-learnings.md`.\n\n---\n\n## 4. Qualification of an inbound DM or comment\n\nA DM or comment is repliable only if **all** of:\n\n- The conversation thread has at least one message from the user (not a pure share/forward).\n- The message is in a language you can answer in (otherwise: skip, do not auto-translate).\n- The message is a real question or qualified intent in your domain (not a vent, not a meme, not a request for free expert-grade work).\n- Phase B authorized for brand-mentioning replies (see §3).\n- The same user has not been sent a templated reply in the last 7 days (`tiktok-reply-log.md`).\n- Not a competitor or a troll on first read of the user's recent posts.\n\nIf any check fails: skip. Document why in the recap.\n\n### Read the full context before answering\n\nFor DMs: read **the entire conversation** (`[data-e2e=\"chat-item\"]`), not just the latest message. People often add critical context across 2-3 messages.\n\nFor comments: read the parent video caption AND the comment thread up to the target comment. A reply that ignores the obvious context reads as a bot.\n\n---\n\n## 5. Reply templates\n\n### Phase A (warming, NO brand mention)\n\n- 1-3 sentences, conversational, genuinely helpful.\n- Match the comment / DM tone.\n- Never expert-grade advice (medical, legal, financial).\n- Never link to anything.\n- Never sign with anything that hints at a profession.\n\nExample structure (comment):\n```\n[Direct answer or empathy hook]. [One concrete tip from common sense].\n```\n\n### Phase B (brand-aware, expert)\n\nDefault mode = **pedagogical**, not promotional.\n\nDM reply structure (≤ 6000 chars but aim for ≤ 600):\n```\n[Acknowledge the situation in 1 sentence, neutral tone.]\n\n[General rule / framework in 2-3 short paragraphs. No exact quote of statute. No price.]\n\n[Concrete next step — point to <PRIMARY_CTA>. NEVER list multiple channels.]\n```\n\nComment reply structure (≤ 200 chars):\n```\n[1 line of contextual acknowledgement]. [Indirect signal — \"feel free to DM us\" / \"form on bio\" — never paste a URL].\n```\n\n### Brand link policy\n\n**ZERO outgoing links inside DMs or comments themselves.** TikTok actively dampens reach for posts containing third-party URLs in any user-visible text. Indirect signaling only:\n\n- Bio link in your profile is the only authorized URL surface.\n- Comment / DM body: never paste a URL, never paste a brand name in a way that triggers the \"may be unsafe\" toast.\n- Never paste an external URL in a video caption either — the FYP penalty is real.\n- Maximum 1 indirect mention per reply (e.g. \"we have an app for that\" — no name).\n\n### The encoding trap (critical — see §9 for the full story)\n\nIf your sending path uses `cliclick t:'...'` for comment posting, **strip all accented characters and emoji** from the rendered string before sending. The macOS keystroke path silently drops or replaces non-ASCII characters in some locales. Replace `é → e`, `à → a`, `' → '`, `…  → ...` etc. before the type call.\n\nForbidden in any reply:\n- Any URL on `<BRAND_DOMAIN>` (and `www.`, subdomains, shorteners).\n- \"DM me\", \"click my profile\", \"check our site\", \"more info here\", phone numbers, email addresses (in the reply body — bio is the only authorized URL surface).\n- Brand name in the comment body (Phase A) or more than once (Phase B).\n- Promises (\"you will win\", \"guaranteed\", \"in 24h I'll fix it\").\n- Quoting specific past client / customer cases.\n- Emojis (encoding trap + sober tone in regulated fields).\n\n---\n\n## 6. Original posting cadence\n\nPosting cadence is **outside the scope of the live operational crons** described in §9 (those handle reactive DM + comment replies). The recommended rhythm for an account in Phase B is:\n\n- **3-5 short videos / week** (15-45 s), one idea per video, hook in the first 1.5 s.\n- **1 longer video / week** (45-90 s) explaining one concept end-to-end.\n- **Avoid burst posting** (3+ videos within 6 h) — TikTok throttles the burst.\n- **Respect the sound policy**: original sounds and verified-license sounds only. Copyrighted commercial tracks on a Business account = silent mute on the FYP.\n\nHashtag discipline:\n- 3-5 hashtags per video, max.\n- 1 broad (#fyp, #foryou) + 2-3 niche (#yourdomain).\n- Never use banned or shadowbanned hashtags — re-check the `<WORKSPACE_DIR>/memory/tiktok-hashtag-state.md` registry monthly.\n\nApply a posting calendar in `<WORKSPACE_DIR>/memory/tiktok-ideas.md`. Run a Weekly Planning cron that picks the next 5-7 hooks from `tiktok-ideas.md` and confirms none of them duplicates a post in `tiktok-post-log.md` over the last 30 days.\n\n---\n\n## 7. Quotas (hard limits)\n\n| Action | Limit |\n|--------|-------|\n| Replies (DM) per 24 h | 30 max |\n| Replies (DM) per Reply Pass run | 4 max |\n| Replies (comment) per 24 h | 40 max |\n| Replies (comment) per Reply Pass run | 6 max |\n| Outbound first-contact DMs / day | 0 (Phase A) — 5 (Phase B, only if user opted-in) |\n| Follow / day | 20 max (Phase B), 5 max (Phase A) |\n| Unfollow / day | 20 max |\n| Actions in same video thread | min 30 min apart |\n| Actions globally (any) | min 1 min apart |\n| Same opening phrase across replies | never (see §8) |\n\nQuota tracking: read `tiktok-reply-log.md` at the start of every run, count entries with timestamps in the last 24 h, abort early if quotas already met.\n\n### Cron interleaving\n\nTo avoid two crons hammering the Business Suite at the same time:\n\n- **DM Check** runs at `:00, :15, :30, :45`.\n- **Comment Check** runs at `:03, :18, :33, :48` (offset by 3 min).\n\nNever fuse the two into one cron — a single long-running cron is more likely to hit a TikTok soft block.\n\n---\n\n## 8. Anti-spam triggers (words and patterns to avoid)\n\n### Content\n\n| Avoid | Use instead |\n|-------|-------------|\n| \"DM me\", \"check my profile\", \"link in bio\" | (bio link does the work — don't point at it) |\n| `<BRAND_NAME>` repeated > 1 time in same reply | (mention once, max) |\n| \"free consultation\", \"first call free\" | (no marketing language) |\n| Phone number, email, URL | (never in body) |\n| Emojis | (regulated / sober fields — drop them. Also: encoding trap on macOS — see §9) |\n| All caps for emphasis | (use italics sparingly, prefer plain text) |\n| Bit.ly / tinyurl / shorteners | (TikTok flags shorteners aggressively) |\n\n### Behavioral\n\n- Replying within 30 s of comment creation → bot-like, wait ≥ 2 min.\n- Same opening phrase across multiple replies → flagged (vary openings).\n- > 6 actions in 10 min → temporary rate limit.\n- Posting > 3 videos within 6 h → burst penalty on the FYP.\n- Following > 25 users / day → 24 h action block.\n- Mass-mentioning users (`@user @user @user` in a single reply) → spam classifier triggers.\n- Mass-DMing identical messages → near-instant temporary ban.\n\n---\n\n## 9. Human-like UI flow (the only thing that actually works)\n\nTikTok's Business Suite ships heavy front-end protections: iframes everywhere, two stacked textboxes after a \"Reply\" click, IME-aware focus management, and a non-trivial composer that ignores synthetic events. The only flow that survives is **physical-input simulation** — real mouse clicks via `cliclick` on macOS (or `xdotool` on Linux), real keystrokes, real dwells. This is the TikTok equivalent of Reddit's reCAPTCHA dodge: not a bypass, just real human signals.\n\n### DM send flow (validated)\n\nURL: `https://www.tiktok.com/messages?lang=<your-lang>` (redirects to `business-suite` if Business account, with an iframe wrapping the conversation pane).\n\n1. **Pre-flight session check** (see §2). Confirm `uniqueId` is not null.\n2. **Navigate to `/messages?lang=<lang>`**. Wait for the redirect to complete (≥ 1.5 s dwell).\n3. **Detect iframe** via JS: `document.querySelectorAll('iframe').length > 0`. If yes, scope all subsequent queries to that iframe.\n4. **List unread conversations**: query `[data-e2e=\"chat-list-item\"]` (within iframe).\n5. For each unread:\n   a. Click the conversation tile.\n   b. **Read all visible messages** `[data-e2e=\"chat-item\"]` — full context before answering.\n   c. Qualify the lead (see §4).\n   d. Draft the reply (≤ 6000 chars).\n6. **Send the reply** (the sequence below is the only one validated end-to-end):\n   ```\n   1. subprocess.run([\"pbcopy\"], input=msg.encode(\"utf-8\"))\n   2. Page.bringToFront via CDP\n   3. Compute the textbox's screen coordinates (see §9.3)\n   4. cliclick c:X,Y                                     ← MANDATORY physical click in textbox\n   5. time.sleep(0.5)\n   6. osascript -e 'tell app \"System Events\" to keystroke \"v\" using command down'\n   7. time.sleep(1)                                      ← wait for the Send button to become enabled\n   8. Compute screen coords of [data-e2e=\"message-send\"]\n   9. cliclick c:screenX,screenY\n   10. Verify textbox is empty\n   ```\n\nThe **physical click on step 4 is non-negotiable**: without it, the macOS clipboard paste lands in whichever frame currently has focus, which is rarely the message textbox.\n\n### Comment reply flow (nested — validated)\n\nURL: `https://www.tiktok.com/business-suite/comments`.\n\nStructure of the page:\n- Video list **in the main DOM** on the left, with a badge \"N nouveaux commentaires\" / \"N new comments\" per video.\n- Selected video pane on the right, with **two iframes**: `iframe[0]` = messages (always present in background), `iframe[1]` = comments.\n\n1. Scan the video list in the main DOM, regex `/(\\d+) (nouveau|new)/` (not \"Aucun\" / \"No\").\n2. For each video with new comments:\n   a. Click the video tile.\n   b. Wait 2 s for `iframe[1]` to load.\n   c. Read `[data-e2e=\"comment-level-1\"]` + usernames `[data-e2e=\"comment-username-1\"]`.\n   d. Qualify the comment (see §4).\n   e. **Click `[data-e2e=\"comment-reply-1\"]`** of the target comment via JS scoped to `iframe[1]`. Wait 1.5 s.\n3. **The two-textbox trap**: after clicking \"Reply\", two textboxes exist on the page:\n   - **INLINE textbox** (small `vpY`) = the nested reply textbox **← USE THIS ONE**.\n   - **GENERAL textbox** (large `vpY` near the bottom) = the page-level \"Add a comment\" box. Posting here creates a *new* top-level comment, not a reply.\n4. Always pick the textbox with the **smallest** `vpY`. Sample selector:\n   ```python\n   textboxes = [...]  # all contenteditable nodes with their viewport coords\n   inline_tb = min(textboxes, key=lambda t: t['vpY'])\n\n   post_btns = [b for b in all_post_btns if not b['disabled']]\n   closest = min(post_btns, key=lambda b: abs(b['vpY'] - inline_tb['vpY']))\n   ```\n5. **Type the message** (not paste) for comments:\n   ```\n   1. Compute screen coords of inline_tb\n   2. cliclick c:X,Y                  ← physical click in INLINE textbox\n   3. time.sleep(0.5)\n   4. cliclick t:'message_ascii_only' ← TYPE, do not paste\n   5. time.sleep(1)\n   6. Compute screen coords of the closest enabled comment-post button\n   7. cliclick c:X,Y\n   ```\n6. **Verify the reply landed under the parent comment** — re-snapshot and look for your username in the immediate children of the parent.\n\n### 9.3 Computing screen coordinates from viewport\n\nThe browser CLI returns viewport coordinates (`vpX`, `vpY`) inside the page. To click via `cliclick`, you need screen coordinates:\n\n```\nscreenX = window.screenX + viewportX\nscreenY = window.screenY + (outerHeight - innerHeight) + viewportY\n```\n\nThe `(outerHeight - innerHeight)` term accounts for the Chrome top bar. On a typical macOS setup with the default Chrome chrome: `chrome_bar ≈ 87`.\n\nYou can fetch these via:\n\n```js\nconst m = { sX: window.screenX, sY: window.screenY, ih: window.innerHeight, oh: window.outerHeight };\n```\n\n### What does NOT work (and why)\n\n| Method | Why it fails |\n|---|---|\n| `document.execCommand('insertText', false, msg)` | Visually empties the textbox after typing but the message never reaches the send pipeline (TikTok's composer reads from a controlled React state, not from the DOM). |\n| `cliclick kd:cmd t:v ku:cmd` | Types the literal letter \"v\" instead of pasting (the cmd keydown is released before the t:v fires). |\n| `cliclick kp:return` to send | TikTok's composer ignores the Enter key in DMs and comments; you must click the send button. |\n| `osascript ... keystroke \"v\" using command down` for comments | Pastes into the **page-level** focus, which is rarely the inline reply textbox after a \"Reply\" click. Use `cliclick t:'...'` for comments. |\n| Picking the GENERAL textbox (largest `vpY`) for a nested reply | Posts a top-level comment instead of a nested reply. |\n\n### Exit codes (recommended convention for your script)\n\n| Code | Meaning | Recap action |\n|------|---------|--------------|\n| 0 | DM / comment sent, verified | `status: ok`, log the reply |\n| 1 | Fatal error (UI ref missing, iframe absent, send button never enabled) | `status: error`, attach screenshot |\n| 2 | Soft block / \"Tap to retry\" toast | `status: blocked`, stop, alert user |\n| 3 | Session / login KO | `status: blocked`, alert user to re-login |\n| 4 | Comment posted but landed in wrong textbox (top-level instead of nested) | `status: partial`, mark for manual cleanup |\n\n### Gotchas (real ones, costly to discover)\n\n- **The `cliclick t:` accent encoding trap**: `cliclick t:'message'` on macOS drops or replaces accented characters in some locales (notably `fr_FR.UTF-8`). Strip accents and emoji from the rendered message before typing. Same family of bug as Reddit's `LC_NUMERIC` trap, different symptom: instead of dwells failing silently, characters disappear from the visible reply.\n- **iframe focus drift**: clicking inside the iframe via JS does not always give focus to the inner document. Always do a physical `cliclick` after navigating, before pasting/typing.\n- **Two iframes on the comments page**: `iframe[0]` is the messages background, `iframe[1]` is the comments. Always scope queries to `iframe[1]` on `/business-suite/comments`.\n- **The \"Send\" button has multiple instances**: pick the one with the smallest absolute `vpY` distance from your textbox, and verify it is `enabled` before clicking.\n- **Cron timeout**: bump per-job timeout to **at least 1200 s** for posting crons — a 120 s default will kill a successful flow.\n- **Save debug artifacts**: screenshots + page snapshots at each step in a scratch dir (e.g. `/tmp/<your-namespace>/tiktok/`). They are gold when a comment silently lands in the wrong box.\n\n### When the UI changes\n\nTikTok ships UI changes regularly. When the `data-e2e` attributes start returning null on selectors that used to work:\n\n1. Take a manual snapshot via your browser CLI.\n2. Inspect the new attribute names.\n3. Update the selector map.\n4. Re-test on your own account's most recent post (safe, low traffic) before re-enabling the live cron.\n\n---\n\n## 10. Hashtag / sound state management\n\nFile: `<WORKSPACE_DIR>/memory/tiktok-hashtag-state.md`\n\nFor each hashtag used in `<NICHE_KEYWORDS>`:\n\n- Last 5 videos that used it + reach (views, watch-time %).\n- Shadowban check (search the hashtag in incognito — does your video appear?).\n- Banned / sensitive flag (cross-check with [hashtagsforlikes](https://hashtagsforlikes.co) or similar — keep a manual override list).\n\nFile: `<WORKSPACE_DIR>/memory/tiktok-sound-state.md`\n\nFor each sound used:\n\n- Original or licensed (Business accounts can ONLY use commercial-license sounds).\n- Reach with that sound vs your baseline.\n- Sound ID / link.\n\nUpdate at the end of every posting run.\n\n---\n\n## 11. Recovery & blockers\n\n| Issue | Action |\n|-------|--------|\n| `status: stopped` | Report in recap: \"Chrome <BROWSER_PROFILE> stopped, user action required (relaunch Chrome on port <BROWSER_PORT>)\". Stop. |\n| Session API returns `ok: false` | Report login expired. Stop. |\n| Business Suite redirects to `/login` | Same as login expired. Stop. |\n| HTTP 429 from `/api/user/detail/self/` | Stop the run, report \"rate limited\", wait next scheduled run. |\n| Captcha / \"puzzle\" challenge | Stop. Never attempt to solve. Report for user action. |\n| \"Tap to retry\" toast after sending | Soft block. Pause the role for ≥ 1 h. Report. |\n| Comment posted but lands as a top-level (not a reply) | Auto-flag for human cleanup. Pause comment cron for 30 min. |\n| Comment removed by TikTok < 10 min after publish | Auto-freeze the entire comment cron for 6 h. Append cause to `tiktok-learnings.md`. |\n| `evaluate` returns null with no error | Likely off-tiktok.com page → navigate first, retry once. |\n| Account banner \"your account is at risk of restriction\" | Flip to Phase A immediately. Disable all reply crons until manual review. |\n\n---\n\n## 12. Mandatory recap (alert channel + memory)\n\nAt the end of each cron:\n\n**Alert channel (Telegram / Slack / Discord) — final run message**:\n```\n[Job name] — [status: ok|partial|blocked|skipped]\nDMs handled: [N or \"—\"]\nComments handled: [N or \"—\"]\nHot leads: [list short OR \"—\"]\nPhase: [A|B]\nBlockers: [text OR \"—\"]\nNext action: [1 line]\n```\n\n**Memory** — append to `<WORKSPACE_DIR>/memory/tiktok-recaps.md`:\n```\n## YYYY-MM-DD HH:MM TZ — <job-id> — status: <status>\n- Job: <description>\n- Phase: A|B\n- DMs sent: <N>\n- Comments sent: <N>\n- Hot leads: <list or \"—\">\n- Blockers: <text or \"—\">\n- Next useful action: <1 line>\n```\n\nIf no action was taken: say so clearly (`no DM/comment sent, reason`).\n\n---\n\n## 13. Memory files inventory\n\nLocated at: `<WORKSPACE_DIR>/memory/`\n\n| File | Purpose | Update cadence |\n|------|---------|----------------|\n| `tiktok-recaps.md` | Per-run logs (mandatory append) | Every cron run |\n| `tiktok-post-log.md` | Videos published (URL, hook, date, views, watch %) | Each posting run |\n| `tiktok-reply-log.md` | Replies sent (target user, type DM/comment, URL, date, removed?) | Each reply pass |\n| `tiktok-state.md` | Daily phase + warning flags + follower count | Daily Metrics Recap |\n| `tiktok-hashtag-state.md` | Per-hashtag reach + shadowban check | After any posting run |\n| `tiktok-sound-state.md` | Per-sound reach + license info | After any posting run |\n| `tiktok-ideas.md` | Content backlog for new videos | Weekly Planning + ad hoc |\n| `tiktok-learnings.md` | Patterns observed (what got reach / removed) | Weekly Planning + ad hoc |\n| `tiktok-alerts-sent.md` | Anti-doublon log for hot-lead alerts (one row per user) | Each alert |\n\n---\n\n## 14. Account identity guardrails\n\nThis account exists to be **useful in public** with the brand as a *background* signal.\n\n- The handle can carry the brand if your brand is the operating identity (legal/medical/B2B). Personal accounts: keep it neutral.\n- Profile bio: one short generic line + the single `<PRIMARY_CTA>` link. No phone, no email in the bio.\n- Never reply to a DM that asks for expert-grade advice on a specific case — direct the person to `<PRIMARY_CTA>` via a standard polite reply.\n- Never share private case details, names, or numbers — even if the user shares theirs first.\n- Never reuse strict templates from your CRM as TikTok content (they read as marketing).\n- Never promise an outcome.\n- Never charge or solicit payment via TikTok DM.\n\nIf someone asks \"are you a bot?\": answer once with a sober, ambiguous line (`\"I'm part of the team — happy to help\"`) and stop. Do not confirm AI use — and never lie outright if pressed.\n\n---\n\n## 15. Phase A → Phase B transition\n\nWhen the Daily Metrics Recap detects (account_age_days ≥ 30) AND (last_warning_days_ago > 14) AND (followers ≥ 500):\n\n1. Append to `tiktok-state.md`: `YYYY-MM-DD - phase=A - PHASE_B_THRESHOLD_REACHED`.\n2. Send a distinct alert: `🎉 TikTok account in Phase B — enable comment-reply + outbound-DM crons (jobs.json: flip enabled=true)`.\n3. Do **NOT** auto-flip the crons (manual user action — reduces risk of premature B-phase posting).\n\nOnce Phase B is enabled by the user:\n- First week: cap reply pass at **2 replies/run** (not the full 6) — soft ramp.\n- Keep posting cadence steady (no burst).\n- Once 14 days in Phase B without warning: full doctrine quotas (§7).\n\n---\n\n## 16. Stability discipline\n\n- Read the UI before clicking.\n- One click → verify with a snapshot.\n- Do not stack clicks.\n- Close stale tabs at the end of every run.\n- Return to the role's default page when done.\n- Never silently fake a successful action — always verify the reply landed in the right thread / textbox.\n\n**Better silence than spam. Better a blockage report than a fake success.**\n\n---\n\n## 17. First-run checklist\n\nBefore enabling any cron, run through this checklist:\n\n- [ ] Section 0 placeholders filled in your local copy / agent memory.\n- [ ] TikTok account is **Business** or **Creator** (Business Suite UI requires it).\n- [ ] Profile bio is one short line + a SINGLE `<PRIMARY_CTA>` link.\n- [ ] Browser profile launched at `http://127.0.0.1:<BROWSER_PORT>` and logged in.\n- [ ] `/api/user/detail/self/` returns `ok:true` and a non-null `uniqueId` (login verified).\n- [ ] `<WORKSPACE_DIR>/memory/` directory exists with the 9 memory files (see §13) — empty is fine, scripts append.\n- [ ] Alert channel (Telegram / Slack / Discord) webhook tested with a \"hello\" message.\n- [ ] `cliclick` installed (`brew install cliclick`) and macOS Accessibility permission granted to the terminal that runs the cron.\n- [ ] Phase A confirmed: account < 30 d OR < 500 followers OR recent warning → only inbound-DM cron enabled.\n- [ ] At least 14 days of organic posting (one video/day, no automation) **before** enabling reply crons, even if Phase B-eligible by metrics.\n\nA bash one-liner to init the memory files:\n\n```bash\nmkdir -p \"<WORKSPACE_DIR>/memory\" && cd \"$_\" && touch tiktok-recaps.md tiktok-post-log.md tiktok-reply-log.md tiktok-state.md tiktok-hashtag-state.md tiktok-sound-state.md tiktok-ideas.md tiktok-learnings.md tiktok-alerts-sent.md\n```\n\n---\n\n## 18. FAQ\n\n**Q: Do I need OpenClaw to use this skill?**\nA: No. OpenClaw browser CLI is the example stack — the doctrine works with Playwright, Puppeteer, Chrome MCP, or any CDP-capable tool. The `cliclick` step in §9 is macOS-specific; on Linux replace with `xdotool`; on Windows use the `SendInput` win32 API or a wrapper like AutoHotkey.\n\n**Q: Can I use this skill for multiple TikTok accounts?**\nA: Yes — clone the workspace dir per account. Each account gets its own `memory/` and its own browser profile. Use a different `<BROWSER_PORT>` per account so they don't collide.\n\n**Q: Does this work with the official TikTok API (Display / Content Posting)?**\nA: Partially. The official API covers video upload and basic profile reads but does NOT cover comment-reply or DM-reply — exactly the surfaces this skill operates on. The UI flow in §9 is the only reliable path until TikTok ships an official DM/comment-reply API.\n\n**Q: What about TikTok Shop / Affiliate / Live?**\nA: Out of scope. This skill covers DMs, comment replies, and FYP-eligible organic posting only. Shop and Live have separate UI and separate sanction patterns.\n\n**Q: How does this interact with the \"Action blocked\" toasts?**\nA: Treat them as soft blocks (exit code 2 in §9). Pause the role for ≥ 1 h, alert the user, do not retry inside the same cron run. If the toast says \"we'll let you know when you can do this again\", flip the account to Phase A and require a manual re-evaluation.\n\n**Q: Can I run multiple crons in parallel?**\nA: Within one account, no — the cron interleave in §7 (3-minute offset) is there because the Business Suite session can only handle one operation at a time without producing inconsistent UI state. Across accounts, yes, but use different browser profiles and different `<BROWSER_PORT>`.\n\n**Q: What if my account is banned or shadowbanned?**\nA: Stop everything. Do not appeal automatically — TikTok's appeal flow is sensitive to repeated automated submissions. Manual review only. Document the exact last 20 actions in `tiktok-learnings.md` so the post-mortem can identify the trigger.\n\nFile v1.0.0:README.md\n\n# TikTok Account Operations\n\n> Operating doctrine for TikTok automation — careful, value-adding creator pattern with role separation, business-suite based DM/comment ops, human-like UI flow, strict anti-shadowban quotas, and recovery patterns.\n\n[![License: MIT-0](https://img.shields.io/badge/License-MIT--0-blue.svg)](https://opensource.org/licenses/MIT-0)\n[![Version](https://img.shields.io/badge/version-1.0.0-green)](#changelog)\n\nA Claude Code / OpenClaw skill for running TikTok brand accounts safely. Battle-tested in a regulated-field operation, ported to be domain-agnostic.\n\n**Why most TikTok automation gets throttled (or banned)**: it treats `/business-suite/comments` like a normal DOM, types via synthetic events, and ignores the iframe + textbox-trap. This doctrine doesn't.\n\n---\n\n## What's in the box\n\n- **3-role mental model** for any TikTok account (`tk-post` / `tk-engage` / `tk-stealth`)\n- **Algorithm-trust-phased posting**: Phase A safety net (account < 30 days OR recent CG notice, zero outbound brand-aware) → Phase B (full doctrine) — with **manual override path** for established brands\n- **Zero-outgoing-link policy** in comments and DMs (TikTok dampens reach hard on external URLs in user-visible text)\n- **Human-like UI flow** — the only thing that actually works inside `/business-suite/`:\n  - Physical `cliclick` mouse clicks (the synthetic-event composer is non-functional)\n  - The two-textbox trap on the comments page (smallest-`vpY` rule for nested replies)\n  - The `cliclick t:` accent encoding trap (silently drops accents in some locales — same family as Reddit's `LC_NUMERIC` trap)\n  - Computing screen coordinates from viewport\n  - Recommended exit-code convention\n- **Strict reply qualification**: language, context-read requirement, troll filter, 7-day per-user cap\n- **Hard quotas** calibrated under TikTok soft-block thresholds (replies/day, follow/day, cron-interleave 3-min offset)\n- **Anti-spam triggers**, both content (banned phrases, shorteners-banned) and behavioral (no reply within 30s, no >6 actions in 10 min, no burst posting)\n- **Sound + hashtag state tracking**: auto-detect shadowbanned hashtags, license-compliance for Business accounts\n- **Full recovery playbook**: `status:stopped`, HTTP 429, captcha challenge, \"Tap to retry\" soft-block, comment-removal automod, account warning banner\n- **Memory file inventory** for stateful agents\n- **Mandatory recap pattern** (alert channel + memory)\n\n---\n\n## Install\n\n### Via ClawHub\n\nThe skill is designed to be published on ClawHub — install in one click from your agent once published.\n\n### Manual copy\n\n```bash\nmkdir -p ~/.claude/skills/tiktok-account-operations\ncp SKILL.md ~/.claude/skills/tiktok-account-operations/\n```\n\nOr for OpenClaw:\n\n```bash\nmkdir -p ~/.openclaw/skills/tiktok-account-operations\ncp SKILL.md ~/.openclaw/skills/tiktok-account-operations/\n```\n\n---\n\n## Quick start\n\n1. Open `SKILL.md` and fill the placeholders in **Section 0** (brand, domain, handle, browser profile, port, niche keywords, primary CTA, workspace dir).\n2. Confirm your TikTok account is **Business** or **Creator** — the `/business-suite/` UI requires it.\n3. Install `cliclick` (`brew install cliclick`) and grant macOS Accessibility permission to your cron's terminal.\n4. Wire your crons: DM check at `:00, :15, :30, :45`, Comment check at `:03, :18, :33, :48` — **never fuse them**.\n5. **Start in Phase A** until account_age ≥ 30 days AND followers ≥ 500. Don't shortcut.\n\nShell snippets in the skill use the [OpenClaw](https://openclaw.ai) browser CLI as an example automation stack. Swap them for Playwright, Puppeteer, Chrome MCP, or any CDP-capable tool you prefer — the doctrine is stack-agnostic. The `cliclick` step is macOS-specific; on Linux use `xdotool`; on Windows use `SendInput` / AutoHotkey.\n\n---\n\n## Who is this for\n\nAny niche where the brand needs to be a *background* signal behind genuinely useful comments and DMs:\n\n- **Legal services** (origin of the doctrine — works under strict bar association rules)\n- **Medical / health** practitioners with public-content strategies\n- **Financial advisors** subject to compliance constraints\n- **SaaS / B2B founders** doing community-led growth\n- **Creators / agencies** managing brand accounts on behalf of clients\n\nAnywhere shadowban = reach death.\n\n---\n\n## Repository structure\n\n```\ntiktok-account-operations/\n├── SKILL.md         # the skill (full doctrine)\n├── README.md        # this file\n├── LICENSE          # MIT-0\n└── CHANGELOG.md     # version history (TBD)\n```\n\n---\n\n## Companion skills\n\n- **[reddit-account-operations](https://github.com/AlexBloch-IA/reddit-account-operations)** — same doctrine family, ported to Reddit with karma-phased posting and sub-state tracking.\n- **[twitter-account-operations](https://github.com/AlexBloch-IA/twitter-account-operations)** — same doctrine, ported to X/Twitter.\n- **instagram-account-operations** — for Meta Business Suite-based IG ops.\n- **facebook-account-operations** — for FB Page automation.\n- **whatsapp-account-operations** — for BSP-API-driven WhatsApp Business ops (the downstream conversion channel for TikTok leads).\n\n---\n\n## License\n\nReleased under **MIT-0** (MIT No Attribution). Use, fork, adapt, redistribute. No attribution required.\n\nFile v1.0.0:_meta.json\n\n{\n  \"ownerId\": \"kn7f6shzecv3qv3wrr074s49f986ybe1\",\n  \"slug\": \"tiktok-account-operations\",\n  \"version\": \"1.0.0\",\n  \"publishedAt\": 1779140240642\n}\n\nFile v1.0.0:skill-card.md\n\n## Description: <br>\nOperating doctrine for TikTok account automation with role separation, Business Suite DM and comment workflows, conservative quotas, account-safety checks, and recovery patterns. <br>\n\nThis skill is ready for commercial/non-commercial use. <br>\n\n## Publisher: <br>\n[alexbloch-ia](https://clawhub.ai/user/alexbloch-ia) <br>\n\n### License/Terms of Use: <br>\nMIT-0 <br>\n\n\n## Use Case: <br>\nExternal operators, creators, agencies, and developers use this skill to configure and supervise scheduled TikTok brand-account activity across messages, comments, posting cadence, state tracking, and recovery workflows. It is intended for accounts where account safety and long-term reach matter more than raw automation volume. <br>\n\n### Deployment Geography for Use: <br>\nGlobal <br>\n\n## Known Risks and Mitigations: <br>\nRisk: Recurring logged-in DM and comment automation can trigger platform enforcement, account loss, or reach throttling. <br>\nMitigation: Use the skill only on accounts you control, review workflows before scheduling them, keep quotas conservative, and stop automation after warnings or soft blocks. <br>\nRisk: The workflow requires access to a logged-in browser profile and OS-level Accessibility or UI-control permissions. <br>\nMitigation: Run it from an isolated browser profile in a low-privilege environment, and revoke browser or Accessibility access when automation is not actively needed. <br>\nRisk: Human-like UI control and anti-detection tactics increase platform-policy and account-safety sensitivity. <br>\nMitigation: Avoid unsolicited outbound DMs and comments, require human review for scheduled workflows, and prefer manual intervention for appeals, account warnings, and unusual UI changes. <br>\n\n\n## Reference(s): <br>\n- [ClawHub skill page](https://clawhub.ai/alexbloch-ia/tiktok-account-operations) <br>\n- [Publisher profile](https://clawhub.ai/user/alexbloch-ia) <br>\n- [OpenClaw](https://openclaw.ai) <br>\n- [TikTok Business Suite Messages](https://www.tiktok.com/business-suite/messages) <br>\n- [TikTok Business Suite Comments](https://www.tiktok.com/business-suite/comments) <br>\n\n\n## Skill Output: <br>\n**Output Type(s):** [Guidance, Markdown, Configuration, Shell commands] <br>\n**Output Format:** [Markdown guidance with YAML configuration examples and shell command snippets] <br>\n**Output Parameters:** [1D] <br>\n**Other Properties Related to Output:** [Includes account-specific placeholders, schedules, quotas, state files, and recovery procedures; workflows should be reviewed before enabling automation.] <br>\n\n## Skill Version(s): <br>\n1.0.0 (source: server release metadata) <br>\n\n## Ethical Considerations: <br>\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment. <br>","readmeExcerpt":"Skill: TikTok Account Operations Owner: alexbloch-ia Summary: Run a TikTok Business account from cron — inbound DM/comment replies, quotas, phase gates, stop-on-block. Use when scheduling TikTok replies. Trigger on \"tik... Tags: account-safety:2.0.1, agent:2.0.1, anti-shadowban:2.0.1, automation:2.0.1, browser-automation:2.0.1, business-suite:2.0.1, cliclick:2.0.1, comments:2.0.1, cron:2.0.1, dm:2.0.1, doctrine:2.0.1","codeSnippets":[],"executableExamples":[{"language":"yaml","snippet":"# <WORKSPACE_DIR>/config.yaml\ntiktok:    { handle: <TIKTOK_HANDLE>, business_account: true, browser_profile: <BROWSER_PROFILE>, browser_port: <BROWSER_PORT> }\ncta:       { primary: <PRIMARY_CTA> }\ndiscovery: { niche_keywords: <NICHE_KEYWORDS> }\nalerts:    { webhook: null }             # null = no network egress. Opt in explicitly.\nschedule:  { timezone: Europe/Paris,\n             dm_check:       \"0,15,30,45 9-22 * * *\",  # every 15 min\n             comment_check:  \"3,18,33,48 9-22 * * *\",  # +3 min — the two must never overlap\n             browser_health: \"0 * * * *\", daily_recap: \"0 21 * * *\" }  # recap carries the phase gate — do not drop it"},{"language":"bash","snippet":"mkdir -p \"<WORKSPACE_DIR>/memory\" && cd \"$_\" && for f in recaps post-log reply-log state hashtag-state sound-state ideas learnings alerts-sent; do [ -f \"tiktok-$f.md\" ] || touch \"tiktok-$f.md\"; done\n# Output: (silent, idempotent — existing files are never truncated)"},{"language":"bash","snippet":"openclaw browser --browser-profile <BROWSER_PROFILE> status\n# Output: {\"state\":\"running\",\"port\":18801}\nopenclaw browser --browser-profile <BROWSER_PROFILE> evaluate --fn \"async () => { const r = await fetch('https://www.tiktok.com/api/user/detail/self/', {credentials:'include'}); const d = await r.json(); return {ok: d.statusCode === 0, uniqueId: d.userInfo?.user?.uniqueId || null}; }\"\n# Output: {\"ok\":true,\"uniqueId\":\"acmestudio\"}"},{"language":"text","snippet":"[Acknowledge the situation in one neutral sentence.]\n[General framework, 2-3 short paragraphs. No statute quote. No price.]\n[One next step — <PRIMARY_CTA> only. Never list two channels.]"},{"language":"text","snippet":"1. pbcopy ← message on the clipboard, UTF-8       2. Page.bringToFront (CDP)\n3. screen coords of the textbox (see below)\n4. cliclick c:X,Y  ← physical click. Non-negotiable: without it the paste lands\n                     in whatever frame holds focus, rarely the textbox\n5. sleep 0.5    6. osascript -e 'tell app \"System Events\" to keystroke \"v\" using command down'\n7. sleep 1 ← the Send button enables asynchronously\n8. screen coords of [data-e2e=\"message-send\"] → cliclick c:X,Y    9. verify the textbox is empty"},{"language":"python","snippet":"inline_tb = min(textboxes, key=lambda t: t[\"vpY\"])          # nested reply box\npost_btn  = min([b for b in post_btns if not b[\"disabled\"]],\n                key=lambda b: abs(b[\"vpY\"] - inline_tb[\"vpY\"]))"}],"parameters":null,"dependencies":[],"permissions":[],"extractedFiles":[{"path":"SKILL.md","content":"---\nname: tiktok-account-operations\ndescription: Run a TikTok Business account from cron — inbound DM/comment replies, quotas, phase gates, stop-on-block. Use when scheduling TikTok replies. Trigger on \"tiktok dm\", \"tiktok reply\".\nmetadata: {\"clawdbot\":{\"emoji\":\"🎵\",\"requires\":{\"bins\":[\"cliclick\",\"openclaw\"]},\"homepage\":\"https://clawhub.ai/skills/tiktok-account-operations\"}}\n---\n\n# TikTok Account Operations\n\n**The goal is not to reply fast. The goal is to operate one account you own with the care of a real contributor — under TikTok's Terms of Service and Community Guidelines, inside its published limits, and stopped the moment a platform control fires.**\n\n## Access this skill requires — read before anything else\n\nThis skill drives a **logged-in browser and your operating system's input layer**. That is a large amount of power: grant it deliberately, per item, and revoke it when the account is not being operated. Beyond `cliclick`, the snippets assume the OpenClaw browser CLI over CDP plus the macOS built-ins `pbcopy` and `osascript`.\n\n| Access | Why it is needed | If you decline |\n|---|---|---|\n| Logged-in TikTok browser profile (CDP on `127.0.0.1:<BROWSER_PORT>`) | Business Suite has no DM/comment-reply API; the UI is the only surface | Skill is unusable — no fallback |\n| macOS **Accessibility** permission on the cron's terminal (`cliclick`) | TikTok's composer reads React controlled state and ignores synthetic events; only real clicks/keystrokes reach it | Skill is unusable. **This permission lets that terminal click and type anywhere on your machine, not just in TikTok.** Use a dedicated low-privilege user session |\n| Clipboard (`pbcopy` + ⌘V) | Only reliable path for multi-line DM bodies | Use the typing path (§ Sending), ASCII only |\n| Screenshots / DOM snapshots to a scratch dir | Post-mortem when a reply lands in the wrong box | Skip — you lose debuggability, nothing else |\n| Alert webhook (Telegram/Slack/Discord) | Run recaps and blocker alerts | **Leave it unset — this is the default.** Recaps then stay local |\n\n**Network egress: off by default.** The only outbound call this skill makes beyond tiktok.com is the alert webhook, and only if you set `alerts.webhook`. Recaps sent there contain the account handle, counts, and lead usernames — do not point it at a channel wider than your operators.\n\n**Data persisted locally**, nowhere else — `<WORKSPACE_DIR>/memory/`, 9 files. Anything holding a third-party identifier is bounded; nothing here accumulates forever, and **you never store personal details a user sent you**.\n\n| File | Holds | Retention |\n|---|---|---|\n| `tiktok-recaps.md` · `tiktok-reply-log.md` | Run recaps · username + type + timestamp **only — never DM bodies** | **Prune at 90 days** |\n| `tiktok-alerts-sent.md` | A **hash of the username**, not the handle — dedupes lead alerts without keeping who | **Prune at 90 days** |\n| `tiktok-post-log.md` · `tiktok-state.md` · `tiktok-hashtag-state.md` · `tiktok-sound-state.md` · `tiktok-id"},{"path":"_meta.json","content":"{\n  \"ownerId\": \"kn7f6shzecv3qv3wrr074s49f986ybe1\",\n  \"slug\": \"tiktok-account-operations\",\n  \"version\": \"2.0.1\",\n  \"publishedAt\": 1784214115555\n}"},{"path":"skill-card.md","content":"## Description:\n\nRun TikTok Business account operations from cron for inbound DM and comment replies, with quotas, phase gates, and stop-on-block controls.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[alexbloch-ia](https://clawhub.ai/user/alexbloch-ia)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nOperators and developers use this skill to schedule controlled TikTok Business account reply workflows for one account they own. It supports inbound DM and comment handling, local recaps, quota checks, stop conditions, and configuration guidance for browser-based operation.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: The skill requires terminal Accessibility control and a logged-in TikTok browser profile.\n\nMitigation: Use a dedicated low-privilege browser or user session, grant access deliberately, and revoke it when account operations are not running.\n\nRisk: Optional webhook alerts can send account handles, counts, and lead usernames outside the local machine.\n\nMitigation: Leave the webhook unset by default, and only enable it for channels limited to authorized operators.\n\nRisk: TikTok challenges, account warnings, or rate limits indicate that the platform has applied a control.\n\nMitigation: Stop the run, avoid retries or workarounds, alert a human operator, and resume only after manual review.\n\nRisk: Local operation logs include usernames, timestamps, and account activity summaries.\n\nMitigation: Keep logs in the configured workspace, avoid storing DM bodies or personal details, monitor local logs, and prune identifier-bearing logs on the documented retention schedule.\n\n## Reference(s):\n\n- [ClawHub skill page](https://clawhub.ai/alexbloch-ia/skills/tiktok-account-operations)\n- [ClawHub metadata homepage](https://clawhub.ai/skills/tiktok-account-operations)\n\n## Skill Output:\n\n**Output Type(s):** [Text, Markdown, Code, Shell commands, Configuration, Guidance]\n\n**Output Format:** [Markdown with YAML configuration and shell command snippets]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Produces local recap formats, operational checklists, quota guidance, and stop-condition handling instructions.]\n\n## Skill Version(s):\n\n2.0.1 (source: server release metadata)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment."}],"languages":[],"docsSourceLabel":"CLAWHUB","editorialOverview":"Run a TikTok Business account from cron — inbound DM/comment replies, quotas, phase gates, stop-on-block. Use when scheduling TikTok replies. Trigger on \"tik... Skill: TikTok Account Operations Owner: alexbloch-ia Summary: Run a TikTok Business account from cron — inbound DM/comment replies, quotas, phase gates, stop-on-block. Use when scheduling TikTok replies. Trigger on \"tik... Tags: account-safety:2.0.1, agent:2.0.1, anti-shadowban:2.0.1, automation:2.0.1, browser-automation:2.0.1, business-suite:2.0.1, cliclick:2.0.1, comments:2.0.1, cron:2.0.1, dm:2.0.1, doctrine:2.0.1","editorialQuality":{"score":100,"threshold":65,"status":"ready","wordCount":1482,"uniquenessScore":54,"reasons":[]}},"media":{"evidence":{"source":"no-media","verified":false,"confidence":"low","updatedAt":"2026-10-11T06:29:50.076Z","emptyReason":"No screenshots, media assets, or demo links are available."},"primaryImageUrl":null,"mediaAssetCount":0,"assets":[],"demoUrl":null},"ownerResources":{"evidence":{"source":"unclaimed","verified":false,"confidence":"low","updatedAt":"2026-10-11T06:29:50.076Z","emptyReason":"This page has not been claimed by the agent owner."},"hasCustomPage":false,"customPageUpdatedAt":null,"customLinks":[],"structuredLinks":{"docsUrl":null,"demoUrl":null,"supportUrl":null,"pricingUrl":null,"statusUrl":null},"customPage":null},"relatedAgents":{"evidence":{"source":"protocol-neighbors","verified":false,"confidence":"medium","updatedAt":"2026-10-11T08:44:41.571Z","emptyReason":null},"items":[{"id":"8ebccd8e-3863-4187-8355-c3f14e1f9edf","entityType":"agent","canonicalPath":"/agent/iofficeai-aionui","slug":"iofficeai-aionui","name":"AionUi","description":"Free, local, open-source 24/7 Cowork app and OpenClaw for Gemini CLI, Claude Code, Codex, OpenCode, Qwen Code, Goose CLI, Auggie, and more | 🌟 Star if you like it!","url":"https://github.com/iOfficeAI/AionUi","homepage":"https://www.aionui.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-10-09T19:11:12.944Z","createdAt":"2026-02-25T03:38:16.584Z","downloads":null},{"id":"b917f68a-ebff-438e-84f8-3f4b2494c0bc","entityType":"agent","canonicalPath":"/agent/activepieces-activepieces","slug":"activepieces-activepieces","name":"activepieces","description":"AI Agents & MCPs & AI Workflow Automation • (~400 MCP servers for AI agents) • AI Automation / AI Agent with MCPs • AI Workflows & AI Agents • MCPs for AI Agents","url":"https://github.com/activepieces/activepieces","homepage":"https://www.activepieces.com","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-15T02:22:12.426Z","createdAt":"2026-02-25T03:38:12.412Z","downloads":null},{"id":"5cb26759-3a39-483f-94cf-276a98c13bb8","entityType":"agent","canonicalPath":"/agent/cherryhq-cherry-studio","slug":"cherryhq-cherry-studio","name":"cherry-studio","description":"AI productivity studio with smart chat, autonomous agents, and 300+ assistants. Unified access to frontier LLMs","url":"https://github.com/CherryHQ/cherry-studio","homepage":"https://cherry-ai.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-11T14:38:40.986Z","createdAt":"2026-02-25T03:38:19.379Z","downloads":null},{"id":"6f6582d0-5d76-4f0f-b81d-86520247950b","entityType":"agent","canonicalPath":"/agent/copilotkit-copilotkit","slug":"copilotkit-copilotkit","name":"CopilotKit","description":"The Frontend for Agents & Generative UI. React + Angular","url":"https://github.com/CopilotKit/CopilotKit","homepage":"https://docs.copilotkit.ai","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-03-25T09:50:57.846Z","createdAt":"2026-02-25T03:39:14.617Z","downloads":null}],"links":{"hub":"/agent","source":"/agent/source/clawhub","protocols":[{"label":"OpenClaw","href":"/agent/protocol/openclew"}]}}}