TikTok Direct Post Audit Guide

How to pass the TikTok Direct Content Posting API audit and get your own TikTok API keys

This guide walks you through passing TikTok's Content Posting API audit for Direct Post, so you can use your own TikTok API credentials with Outstand (BYOK) without the unaudited restrictions.

Everything here is derived from TikTok's Content Sharing Guidelines - Direct Post API Developer Guidelines. That page is the checklist reviewers work from. Read it in full before you submit - this guide tells you how to satisfy it, not what it replaces.

Why the audit matters

Until your API client passes the audit, TikTok applies two hard restrictions:

RestrictionEffectError code
Private-only postingEvery Direct Post is forced to SELF_ONLY, regardless of what you sendunaudited_client_can_only_post_to_private_accounts
Active creator capAt most 5 distinct creators can publish through your app in a rolling 24 hoursreached_active_user_cap

Passing the audit lifts the private-only restriction and raises the creator cap to a number based on the usage estimates in your application.

You can ship without the audit. MEDIA_UPLOAD (the Outstand default) delivers the media to the creator's TikTok inbox as a draft. It is not subject to the creator cap and has no visibility restriction, because the creator publishes it themselves from the TikTok app. See Post Modes. Only apply for the audit if you genuinely need DIRECT_POST.

If you landed here because Direct Posts started failing with reached_active_user_cap on Outstand's managed TikTok credentials, read TikTok - Direct Post Active Creator Cap first. It covers the immediate workarounds; getting your own app audited is the durable fix.

Before you apply

TikTok will reject the application outright if any of these are missing, before a human ever looks at your UI.

  • The app is in production (not sandbox) and has been tested end to end.
  • video.publish is enabled on the app. Direct Post is unusable without it - video.upload only covers inbox drafts.
  • Your app name, website URL and redirect URI all reference the same brand. If you are using Outstand's callback directly, this is the most common rejection reason - use the proxy callback so TikTok sees your own domain.
  • Your Terms of Service and Privacy Policy URLs are live, HTTPS, and reachable without a login.
  • If you deliver media with PULL_FROM_URL, the hosting domain is verified under URL Properties in the developer portal. Unverified domains fail later with url_ownership_unverified.
  • Your client_secret is server-side only and is not in any public repository.
  • A demo video is recorded and hosted somewhere TikTok's reviewers can open without an account (unlisted YouTube, Loom, or a direct MP4 link all work).

Audit templated answers

TikTok's audit form asks you to describe your integration in free text. The exact field labels change from time to time, so treat these as answer templates keyed to the question being asked, not as a literal form. Replace everything in {{ braces }}.

Reviewers are looking for two things in every answer: that real creators consent to each post, and that you are not bulk-reposting content scraped from elsewhere.

"Describe your product and how it uses the Content Posting API"

{{ Product name }} is a {{ social media scheduling / creator marketing / brand
publishing }} tool used by {{ audience: e.g. brands, agencies and individual
creators }}. Creators connect their own TikTok account via TikTok Login and use
{{ Product name }} to prepare and publish their own original content.

We use the Content Posting API in two modes:

- Direct Post (/v2/post/publish/video/init/ and /v2/post/publish/content/init/)
  when the creator has completed the full post form in our composer - caption,
  visibility, interaction settings and content disclosure - and has explicitly
  pressed Post.
- Inbox upload (/v2/post/publish/inbox/video/init/) when the creator prefers to
  finish the post inside the TikTok app.

Before rendering the post form we call /v2/post/publish/creator_info/query/ to
fetch the creator's nickname, avatar, privacy_level_options,
max_video_post_duration_sec and their comment/duet/stitch settings, and we build
the form entirely from that response. After init we poll
/v2/post/publish/status/fetch/ until we reach a terminal status and report the
real outcome back to the creator.

"Who are your users and how do they obtain the content they post?"

Our users are the creators and the brands who own the accounts they connect.
Content is uploaded by the user from their own device or their own media library
inside {{ Product name }}. We do not import, scrape or repost content from other
platforms, and we do not operate the connected accounts on the users' behalf.
Nothing is ever sent to TikTok without the account owner completing the post
form and pressing Post.

"How many users do you expect to post per day?"

{{ Realistic number }} distinct creators per day at launch, growing to
{{ number }} within {{ timeframe }}.

Answer this honestly. TikTok sets your post-audit creator cap from this number, and asking for a large cap without matching traffic invites scrutiny. You can request an increase later through TikTok's developer support form once the app is audited and in production.

"Describe the user experience for posting content"

1. The creator selects which connected TikTok account to post to. We call
   creator_info/query at that moment and display the returned nickname and
   avatar so it is unambiguous which account will receive the content. If the
   creator cannot currently post, we block posting and prompt them to retry
   later.
2. The creator selects a video or up to 35 photos from their own library. We
   render a preview of exactly what will be posted in a portrait frame matching
   how it will appear on TikTok, with the caption over the video and the
   destination handle shown. Video previews are playable, so the creator can
   watch the file back before committing. No watermark, logo or overlay is added
   to their content. We validate the video against max_video_post_duration_sec.
3. The creator writes their own caption. Any text we prefill remains fully
   editable.
4. The creator selects who can view the post from a dropdown that is populated
   only from privacy_level_options and has no default value.
5. The creator opts in to Comment / Duet / Stitch. All three are off by default
   and are greyed out when creator_info reports that the creator has disabled
   them. For photo posts only Comment is shown.
6. The creator optionally turns on content disclosure and picks "Your brand",
   "Branded content" or both. The toggle is off by default; if it is on and
   neither option is selected, the Post button stays disabled with the hover
   text "You need to indicate if your content promotes yourself, a third party,
   or both." Branded content cannot be combined with a private post - "Only me"
   is disabled with the hover text "Branded content visibility cannot be set to
   private."
7. The creator explicitly agrees to TikTok's Music Usage Confirmation (and
   Branded Content Policy when branded content is selected) via a linked
   consent checkbox, and presses Post.
8. We tell the creator that publishing may take a few minutes and poll
   publish/status/fetch until we reach PUBLISH_COMPLETE, SEND_TO_USER_INBOX or
   FAILED, surfacing the real outcome including the failure reason.

"Per-scope justification"

Answer each enabled scope separately. Reviewers reject applications where a scope is enabled but never demonstrated.

ScopeJustification
user.info.basicDisplay the connected creator's avatar and display name so the user can confirm which account they are posting to.
user.info.profileShow profile link and verification status on the connected accounts screen.
video.uploadSend content to the creator's TikTok inbox as a draft when they choose to finish the post inside the TikTok app.
video.publishPublish directly to the creator's profile after they complete our post form and give explicit consent.
user.info.statsDisplay follower and engagement counts on the creator's own analytics dashboard.
video.listRetrieve the creator's own public posts so we can show per-post metrics for content they published through us.

Video demo template

The demo video is where most applications fail. Reviewers use it to verify every point of the Direct Post guidelines, so every required UI element must be visible on screen and readable. Record at 1080p or better, use a real TikTok account, and do not cut away mid-flow.

Length: 3-5 minutes. Format: unlisted link, one continuous screen recording per flow, no fast-forwarding through the post form.

Shot list

#DurationWhat must be on screenGuideline covered
10:00-0:20Your product's home or dashboard, showing your brand. State the product name and what it does.Intended use
20:20-0:50The full OAuth flow: click "Connect TikTok", the TikTok consent screen with every requested scope visible, then the redirect back showing the account connected.Scope demonstration
30:50-1:10The composer with the account selector open, then selected. Zoom in on the creator nickname and avatar rendered from creator_info.Point 1
41:10-1:40Selecting a video from the user's own library, and the resulting preview. Play the video in the preview for a few seconds so the reviewer can see it is the real file, not a placeholder frame. Show the max duration hint sourced from max_video_post_duration_sec. Make it obvious no watermark or logo is added.Points 1, 5
51:40-1:55Typing a caption by hand. If you prefill anything, show that it is editable.Point 2a
61:55-2:15Opening the privacy dropdown. Pause with it open so the reviewer can see there is no default selected and the options match privacy_level_options. Then select one.Point 2b
72:15-2:35The Comment / Duet / Stitch checkboxes, all unchecked. Toggle one on. If the test account has any disabled, hover it to show the greyed-out state and explanation.Point 2c
82:35-3:05The disclosure toggle off, then on. Check "Your brand" and show the "Promotional content" label. Check "Branded content" and show the "Paid partnership" label. Open the privacy dropdown again and hover "Only me" to show the disabled state and its hover text. Turn the toggle on with nothing selected and hover the disabled Post button to show its hover text.Point 3
93:05-3:20The compliance declaration, close enough to read. Show it change to include the Branded Content Policy when branded content is selected, and that both links open TikTok's policy pages.Point 4
103:20-3:40Pressing Post, then the "may take a few minutes to process" message and any status indicator.Point 5
113:40-4:10The published post live on the TikTok profile or app, with the settings you chose reflected (visibility, comments off/on, the disclosure label).Point 5
124:10-4:40Optional but recommended: repeat shots 3-11 for a photo carousel, showing that Duet and Stitch are absent and only Comment appears.Photo post rules

UI requirements reviewers check

These are the specific things a reviewer clicks on. Each maps to a numbered point in TikTok's Direct Post developer guidelines.

Point 1 - Creator info

  • Call /v2/post/publish/creator_info/query/ when the post page renders and again whenever the selected account changes. A cached response from an earlier session is a fail.
  • Display creator_nickname (and ideally creator_avatar_url) on the post page.
  • If the creator cannot currently post - privacy_level_options is empty or max_video_post_duration_sec is 0 - stop the attempt and prompt them to retry later.
  • Validate the selected video's duration against max_video_post_duration_sec before allowing submission.

Point 2 - Post metadata

2a - Title. The creator must be able to enter their own title. Prefilled text and hashtags are allowed only if they remain editable.

2b - Privacy. A dropdown with no default value, populated exclusively from privacy_level_options. Never hardcode the four enum values - a creator whose account is private will not have PUBLIC_TO_EVERYONE in their options, and sending it fails with privacy_level_option_mismatch. Show human-readable labels:

API valueLabel
PUBLIC_TO_EVERYONEPublic
MUTUAL_FOLLOW_FRIENDSFriends
FOLLOWER_OF_CREATORFollowers
SELF_ONLYOnly me

2c - Interaction toggles. Allow Comment, Allow Duet and Allow Stitch, all unchecked by default. Grey out and disable each one when comment_disabled, duet_disabled or stitch_disabled is true in creator_info. For photo posts show only Allow Comment - Duet and Stitch do not apply.

The UI states "allow" but the API takes the inverse. Send disable_comment: !allowComment. Getting this backwards is a silent correctness bug that the audit will not catch but your users will.

Point 3 - Commercial content disclosure

  • A disclosure toggle, off by default.

  • When on, two checkboxes: Your brand (brand_organic_toggle) and Branded content (brand_content_toggle). At least one must be selected.

  • While the toggle is on and neither box is checked, the Post button must be disabled, with this hover text, verbatim:

    You need to indicate if your content promotes yourself, a third party, or both.

  • Show the resulting label, verbatim:

    SelectionLabel shown
    Your brand onlyYour photo/video will be labeled as 'Promotional content'
    Branded content onlyYour photo/video will be labeled as 'Paid partnership'
    BothYour photo/video will be labeled as 'Paid partnership'
  • Branded content cannot be private. Either disable the "Only me" option with this hover text, verbatim:

    Branded content visibility cannot be set to private.

    or auto-switch visibility to public and tell the user you did. Do not silently allow the combination - TikTok will reject the post.

Point 4 - Compliance declaration

Display one of these strings above the Post button, with the named policies as working links:

ConditionString
Anything other than branded contentBy posting, you agree to TikTok's Music Usage Confirmation
Branded content selectedBy posting, you agree to TikTok's Branded Content Policy and Music Usage Confirmation

Link targets:

  • Music Usage Confirmation - https://www.tiktok.com/legal/page/global/music-usage-confirmation/en
  • Branded Content Policy - https://www.tiktok.com/legal/page/global/bc-policy/en
  • Show a preview of the content before posting. The guideline itself is one sentence - "API Clients should display a preview of the to-be-posted content" - but see below for what a preview that actually passes looks like.
  • Never add a watermark, logo or overlay to the creator's content. This is an instant fail.
  • Explicit consent: content may only be sent to TikTok after the user presses a clear post/share action. Auto-posting on upload, or on a schedule the user did not configure per post, is not acceptable.
  • Warn about processing time. After submitting, tell the user it may take a few minutes for the content to appear.
  • Poll /v2/post/publish/status/fetch/ (or handle webhooks) and report the real outcome. Do not show success when TikTok has not confirmed it.

What the preview should actually look like

  • Make the video playable in the preview. A <video> element the creator can play, scrub and mute before they commit. This is the single clearest way to demonstrate that what the creator reviewed is what gets posted, and it doubles as your own check that the file you are about to send is the one they picked.
  • Render it the way it will appear on TikTok. A 9:16 portrait frame at mobile proportions, with the caption laid over the bottom of the video the way TikTok stacks it, and the account handle shown against it. You do not need to clone TikTok's UI exactly.

Photo posts get the same treatment: show every selected photo in the carousel order TikTok will use, not just the first one, and make it clear which one is the cover.

Gotchas to watch out for

Reading the guidelines gets you most of the way. These are the details that are easy to miss, easy to implement subtly wrong, or that only bite once real creators start posting - worth checking explicitly before you record your demo.

Reset dependent state when the creator changes. Privacy options are per-creator, so a privacy level chosen for one account may not exist for the next. Clear the selection whenever the selected account changes, or you will eventually send a privacy_level that is not in that creator's options and get privacy_level_option_mismatch.

"Cannot post" is a derived condition, not a flag. There is no boolean for it. Treat a creator as unable to post when privacy_level_options is empty or max_video_post_duration_sec is 0. Block posting and offer an explicit retry that refetches creator info - reviewers look for the retry affordance, not just the block.

Enforce the branded-content/private conflict in both directions. Selecting branded content while "Only me" is chosen must be refused; choosing "Only me" while branded content is on must clear branded content. Guarding only one direction leaves a path to an invalid combination that TikTok will reject at publish time.

Branded content wins when both disclosure boxes are checked. Both selected renders as Paid partnership, not two labels and not Promotional content. Derive one label from both checkboxes rather than rendering each independently.

A preview the reviewer cannot verify is barely a preview. The guideline sentence is short enough that it is tempting to satisfy it with a filename and a thumbnail. On a screen recording that is indistinguishable from a placeholder, and it leaves the reviewer taking your word for the one thing they are there to check. Make the video playable and frame it the way it will appear on TikTok - it costs a <video> element and some CSS, and it removes an entire category of reviewer doubt.

Consent must be its own control. The Music Usage Confirmation is not implied by pressing Post. Make it a separate required checkbox that gates the button, swap its text to include the Branded Content Policy when branded content is selected, and make both policy names real links.

A disabled Post button must always explain itself. Drive the tooltip and an inline hint from a single reason string, and return the exact guideline wording where TikTok specifies it. A silently disabled button reads as a bug on a demo video, and the reviewer has no way to know you disabled it deliberately.

Hide direct-post-only controls when you are not direct posting. TikTok ignores every post_info field on an inbox draft - the creator sets caption, visibility and interaction settings in the TikTok app. Rendering those controls for MEDIA_UPLOAD misleads the user into thinking they took effect. Related: record your demo video in Direct Post mode, since that is what is being audited.

Never claim success without a terminal status. Poll to PUBLISH_COMPLETE (direct post) or SEND_TO_USER_INBOX (inbox draft), surface fail_reason on FAILED, and on a polling timeout report an unknown outcome rather than a green success state. TikTok's init call returning 200 means only that the request was accepted.

AI prompt to audit your implementation

Paste this into Claude Code (or any coding agent with access to your repository) to check your implementation against the guidelines before you submit.

You are auditing a codebase against TikTok's Direct Post API developer
guidelines, published at:
https://developers.tiktok.com/doc/content-sharing-guidelines#direct_post_api_-_developer_guidelines

Fetch that page first and use it as the source of truth. Then find every part of
this repository involved in posting to TikTok - the post composer UI, the API
client that calls TikTok's Content Posting API, and any server-side validation -
and verify each requirement below against the actual code. Do not assume a
requirement is met because a variable is named after it; trace the value to
where it is rendered or sent.

For each numbered item, report PASS, FAIL, or PARTIAL, with the file and line
that proves it, and a one-line fix for anything that is not PASS.

Point 1 - Creator info
1.1  /v2/post/publish/creator_info/query/ is called when the post page renders
     AND again whenever the selected creator changes. A cached or
     session-persisted response is a FAIL.
1.2  creator_nickname from that response is displayed on the post page.
1.3  Posting is blocked, with a retry affordance, when the creator cannot post
     (privacy_level_options empty, or max_video_post_duration_sec is 0).
1.4  Selected video duration is validated against max_video_post_duration_sec
     before submission, and the error is cleared when a different video is
     selected.

Point 2 - Post metadata
2.1  The title/caption field is user-editable. Any prefilled text is editable,
     not read-only or forcibly appended at submit time.
2.2  The privacy dropdown has NO default selected value.
2.3  The privacy options are built only from privacy_level_options in the
     creator_info response. Hardcoding the four enum values is a FAIL.
2.4  Privacy selection is reset when the selected creator changes.
2.5  Allow Comment, Allow Duet and Allow Stitch all default to unchecked.
2.6  Each is disabled and visibly greyed out when creator_info reports
     comment_disabled / duet_disabled / stitch_disabled.
2.7  The values sent to TikTok are inverted correctly: disable_comment ===
     !allowComment, and the same for duet and stitch. Check the actual request
     body construction.
2.8  For photo posts, only Allow Comment is rendered - Duet and Stitch are
     absent.

Point 3 - Commercial content disclosure
3.1  The disclosure toggle defaults to off.
3.2  When on, at least one of "Your brand" / "Branded content" must be selected,
     and the post button is disabled until one is.
3.3  The disabled state shows this hover text verbatim:
     "You need to indicate if your content promotes yourself, a third party, or both."
3.4  These label strings appear verbatim:
     - Your brand only:      Your photo/video will be labeled as 'Promotional content'
     - Branded content only: Your photo/video will be labeled as 'Paid partnership'
     - Both selected:        Your photo/video will be labeled as 'Paid partnership'
3.5  Branded content cannot be combined with SELF_ONLY. Either the "Only me"
     option is disabled with the verbatim hover text "Branded content visibility
     cannot be set to private.", or visibility is auto-switched to public with a
     notification. Verify the guard holds in BOTH directions: selecting branded
     content while private is chosen, and selecting private while branded
     content is on.
3.6  "Your brand" maps to brand_organic_toggle and "Branded content" maps to
     brand_content_toggle in the request body. Confirm they are not swapped.

Point 4 - Compliance declaration
4.1  This string appears verbatim for non-branded content:
     "By posting, you agree to TikTok's Music Usage Confirmation"
4.2  This string appears verbatim when branded content is selected:
     "By posting, you agree to TikTok's Branded Content Policy and Music Usage Confirmation"
4.3  The named policies are real links to tiktok.com legal pages.

Point 5 - Content and consent
5.1  A preview of the content is shown before posting, and it renders the media
     the user actually selected rather than a generic placeholder or icon.
5.1a Video previews are playable - a real video element the user can play and
     scrub, not a static poster frame or a play icon with no handler.
5.1b The preview is laid out the way the post will appear on TikTok: portrait
     framing, the caption shown over the content, and the destination account
     handle visible. Report if the preview is a small landscape thumbnail that
     gives no sense of the final post.
5.1c For photo posts, every selected photo is previewable in carousel order and
     the cover photo is identifiable.
5.2  No watermark, logo, or branding overlay is composited onto the user's media
     anywhere in the pipeline. Check any canvas, ffmpeg, or image-processing
     step, not just the preview component. Preview chrome - the phone frame,
     caption overlay, handle - must be decoration around the media only, and
     must never be rasterised into the uploaded file or a generated cover image.
5.3  Content is only sent to TikTok after an explicit user action. There is no
     auto-post on upload path.
5.4  After submission the user is told publishing may take a few minutes.
5.5  /v2/post/publish/status/fetch/ is polled (or webhooks handled) until a
     terminal status, and FAILED surfaces the fail_reason.
5.6  Success is never reported without a terminal success status from TikTok.
     A polling timeout that renders success is a FAIL.

Technical safeguards
6.1  client_secret is only used server-side and never reaches the client bundle
     or a public repository. Check for it in any NEXT_PUBLIC_/VITE_/REACT_APP_
     variable, client component, or committed env file.
6.2  If PULL_FROM_URL is used, media is served from a domain the code can only
     have verified under TikTok's URL Properties, and other hosts are rejected
     before the init call.
6.3  privacy_level is required and validated for DIRECT_POST, and omitted for
     MEDIA_UPLOAD / inbox uploads.

Finish with:
- A table of every FAIL and PARTIAL, ordered by how likely it is to fail the
  audit.
- The exact code changes needed for each.
- Anything in the guidelines that this codebase has no equivalent of at all.