Skip to content

fix(tiktok): default content_posting_method to DIRECT_POST#1687

Closed
giladresisi wants to merge 2 commits into
mainfrom
fix/tiktok-upload-method-default
Closed

fix(tiktok): default content_posting_method to DIRECT_POST#1687
giladresisi wants to merge 2 commits into
mainfrom
fix/tiktok-upload-method-default

Conversation

@giladresisi

@giladresisi giladresisi commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator

Problem

Scheduled TikTok posts were being marked successful in Postiz and Temporal but never published. The postSocial activity returned postId: "missing" with a tiktok.com/messages release URL.

Root cause: the post had content_posting_method: "UPLOAD" instead of DIRECT_POST. UPLOAD does not publish — TikTok's API delivers the video to the account's app inbox, where the user must manually complete it within ~24h or it's discarded. This is TikTok's SEND_TO_USER_INBOX terminal status, which we (correctly) treat as a successful upload.

Why the agent chose UPLOAD: the chat/MCP agent had to supply content_posting_method with no guidance — it's a bare required enum ('DIRECT_POST' | 'UPLOAD') with no description or default, and TikTok's agent @Rules said nothing about it. For a video post, "UPLOAD" reads like the obvious choice. The user never asked for it; the model guessed.

Fix (three layers)

  • @Rules guidance (tiktok.provider.ts): the agent is now told to use DIRECT_POST unless the user explicitly asks to review/edit inside the TikTok app first, and that UPLOAD does not publish.
  • Hard default (integration.schedule.post.ts): a PROVIDER_SETTINGS_DEFAULTS map + buildSettings() helper fills content_posting_method with DIRECT_POST when the key is omitted — applied in both the validation and creation paths — so an omitted value can never land on UPLOAD by accident. The default map is TikTok-only; all other providers reduce to the exact prior behavior.
  • Schema description (tiktok.dto.ts): @JSONSchema({ description }) explains, on the field itself, that UPLOAD never publishes.

Why all three

They cover different populations, and the layers are not redundant:

Consumer Reached by
Internal web agent + external MCP agents @Rules (via integrationSchema), plus the default as a backstop
Public API caller that reads rules from GET /public/v1/integration-settings/:id @Rules
Public API caller that reads only the settings schema @JSONSchema description

Note the default only fills a key that is undefined/''. Because the field is required, an agent nearly always sends something — so the prose rule and the field description are what actually steer it away from UPLOAD; the default is insurance against omission.

The @JSONSchema decorator is metadata only — it registers no validator, so @IsIn([...]) still rejects a missing or invalid value and the field remains required. The dashboard is unaffected: its React form hardcodes DIRECT_POST.

Testing

  • Validated live over MCP: scheduling a TikTok draft omitting content_posting_method now yields DIRECT_POST.
  • Generated-schema check on TikTokDto: description present, enum unchanged, content_posting_method still listed in required.
  • Behavior-preserving for non-TikTok providers (defaults map has only a tiktok entry).
  • Typechecks (backend).

Not covered here

Two other copies of this enum are documented outside this repo and are unreachable from a backend change. Both are being fixed separately:

  • gitroomhq/postiz-docspublic-api/providers/tiktok.mdx describes UPLOAD as "Upload for manual posting"; public-api/openapi.json has a bare enum with no description. (Hand-maintained, not generated.)
  • gitroomhq/postiz-agent — the skill bundles its own PROVIDER_SETTINGS.md, which lists the enum with no explanation. Skill runs read that file rather than calling integrations:settings, so no backend fix reaches them.

Notes

First of two related changes. A follow-up feature branch (agent list/update of scheduled posts) stacks on this one and will be opened as a separate PR.

🤖 Generated with Claude Code

The agent (chat / MCP) had to pick content_posting_method with no
guidance: the setting is a bare required enum with no description or
default, and TikTok's agent rules said nothing about it. For a video
post "UPLOAD" reads like the obvious choice - but UPLOAD does not
publish, it only drops the video into the user's TikTok app inbox,
where it must be completed manually within ~24h or it is discarded.
Users hit this as "post marked successful but never published".

Two layers:
- TikTok @rules text now tells the agent to use DIRECT_POST unless the
  user explicitly asks to review/edit in the app first, and explains
  that UPLOAD does not publish.
- A PROVIDER_SETTINGS_DEFAULTS map + buildSettings() helper fills
  content_posting_method with DIRECT_POST when the key is omitted, in
  both validation and creation, so an omitted value can never land on
  UPLOAD by accident.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@postiz-contribution postiz-contribution Bot added the contribution:approved Approved contributor label Jul 8, 2026
@postiz-contribution

Copy link
Copy Markdown

Contribution-checker quality warning
Heuristic score: 1/100 (low). This is a non-blocking warning surfaced by the project's quality settings.

Heuristics that flagged:

  • Excessive inline code references: 24 inline refs (>3)
  • PR doesn't use the repo's PR template: 5 required checkbox items missing (max 1, match ≥80%)
  • Body adds too many extra headers beyond the template: 4 extra headers (>1)
  • AI watermark phrase: Matched: "Generated with Claude Code"
  • Commit message too long: Longest: 1011 chars

If this is a genuine contribution, please add detail to your PR description and tighten the diff scope before reviewers look at it.

@postiz-agent

postiz-agent Bot commented Jul 8, 2026

Copy link
Copy Markdown

Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues
Licenses 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

`content_posting_method` was exposed to schema consumers as a bare
`enum: ["DIRECT_POST", "UPLOAD"]` with no description, so an LLM asked to
"upload this video to TikTok" would reasonably pick the value literally
named UPLOAD - which never publishes, it only drops the media into the
user's TikTok app inbox where it expires after 24 hours.

Annotate the field with `@JSONSchema({ description })`. This is metadata
only: it is read by class-validator-jsonschema when generating the schema
and registers no validator, so `@IsIn([...])` still rejects a missing or
invalid value and the field stays required. Verified against the
generated schema: `description` is present, `enum` is unchanged, and
`content_posting_method` remains in `required`.

The annotation reaches every `getValidationSchemas()` consumer:
- `integrationSchema` MCP tool (internal web agent + external MCP agents)
- `GET /public/v1/integration-settings/:id` (public API, and the CLI's
  `postiz integrations:settings`)

The dashboard is unaffected - its React form hardcodes DIRECT_POST.

For agents this layers on top of the @rules text in the previous commit:
the settings default only fills an omitted key, and since the field is
required an agent nearly always sends *something*, so the prose rule and
this description are what steer it away from UPLOAD. For public API
callers that read the schema but not `rules`, this is the only in-band
signal.

Precedent for the decorator: whop.dto.ts, discord.dto.ts, listmonk.dto.ts.

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

This comment was marked as duplicate.

@giladresisi

Copy link
Copy Markdown
Collaborator Author

Superseded by #1710.

Same fix, cleaner history. The PROVIDER_SETTINGS_DEFAULTS map in integration.schedule.post.ts has been dropped (TikTok-specific value in a generic tools file; only applied on the MCP path; and redundant, since the field is required so an omitted key is rejected by validation rather than falling through to UPLOAD). In its place, #1710 normalizes a missing content_posting_method to DIRECT_POST inside the provider itself, at publish time.

Closing in favour of #1710 — force-push is blocked on this branch, so the rewritten history could not be pushed here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

contribution:approved Approved contributor

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant