TikTok

Configure TikTok in Outstand with your own API credentials (BYOK), including registering a developer app and OAuth setup.

TikTok is supported in Outstand through the Bring Your Own Key (BYOK) model. This allows you to use your own TikTok API credentials to connect your TikTok accounts and post content through Outstand's unified social media API.

On the TikTok dev portal register a new app. New apps have sandbox and production environments. You can use the sandbox environment to test your integration before going live.

Before you begin, ensure you have:

  • A TikTok developer account
  • A website or landing page for your application
  • Understanding of OAuth 2.0 flows
  • Access to your website's server/backend to set up callback routing (if using proxy solution)

Step 1: Create a TikTok Developer App

  1. Go to the TikTok Developer Portal
  2. Sign in or create a developer account
  3. Navigate to My Apps and click Create App
  4. Fill in the required information:
    • App Name: Your application name (e.g., "My Video Scheduler" or "Outstand Video Scheduler")
    • App Category: Select the appropriate category
    • Website URL: Your application's website URL (must be HTTPS)
    • Terms of Service URL: Your terms of service page (must be HTTPS)
    • Privacy Policy URL: Your privacy policy page (must be HTTPS)

Important: TikTok requires brand consistency. Your app name, website URL, and redirect URI should all reference the same brand. See the Branding Requirements section below for details.

Step 2: Configure OAuth Settings

Understanding Redirect URIs

TikTok requires that your redirect URI matches your app's branding. Since Outstand uses a fixed callback URL (https://www.outstand.so/app/api/socials/tiktok/callback), you have two options:

Set up a proxy callback on your own domain that forwards requests to Outstand. This ensures TikTok sees your domain in the redirect URI, maintaining brand consistency.

Steps:

  1. In your TikTok app, add your redirect URI
  2. Set up a proxy endpoint on your server that forwards to Outstand's callback
  3. See Proxy Callback Implementation below for code examples

Option B: Use Outstand's Callback Directly

If brand consistency isn't critical for your use case:

  1. In your TikTok app, add the redirect URI: https://www.outstand.so/app/api/socials/tiktok/callback
  2. Ensure your app name and website URL mention Outstand or use generic branding

Required Scopes

Outstand requests the following scopes. You must enable all of them in your TikTok app:

Default Scopes:

  • user.info.basic - Access profile info (avatar and display name) - REQUIRED
  • user.info.profile - Read additional profile info (bio, profile link, verification status) - REQUIRED
  • video.upload - Upload a draft to the creator's TikTok inbox (MEDIA_UPLOAD, the default post mode) - REQUIRED
  • video.publish - Publish straight to the profile (DIRECT_POST) - REQUIRED
  • user.info.stats - Read profile engagement statistics (used for account insights)
  • video.list - Read public videos on TikTok (for post metrics)

Note: Outstand requests all 6 scopes above. You must enable every one of them in your TikTok app configuration for the OAuth flow to work.

Step 3: Set Up Sandbox Testing

Before submitting for approval, test your integration using TikTok's sandbox environment:

  1. In your TikTok app, navigate to Sandbox
  2. Add a test user (your TikTok account)
  3. Test the OAuth flow and posting functionality
  4. Ensure all scopes work as expected

Step 4: Submit for Review

TikTok requires app review before you can use production APIs.

If you want to publish straight to a creator's profile (DIRECT_POST), your app must additionally pass TikTok's Content Posting API audit. Until it does, every Direct Post is forced to SELF_ONLY and only 5 distinct creators may publish through your app in a rolling 24 hours.

See TikTok Direct Post Audit for pre-filled application answers, a demo video script, the exact UI strings reviewers check, and an AI prompt to audit your implementation before you submit.

Common Rejection Reasons

TikTok may reject your app if:

  1. Brand Inconsistency: App name, website, and redirect URI don't match

    • Solution: Use the proxy callback approach (see below)
  2. Missing Scope Demonstration: Demo video doesn't show all enabled scopes

    • Solution: Update demo video to show each scope being used
  3. Invalid Website: Website URL doesn't exist or isn't accessible

    • Solution: Ensure your website is live and accessible via HTTPS
  4. Incomplete Demo Video: Video doesn't show the complete user flow

    • Solution: Record a comprehensive demo showing OAuth → posting → results

Proxy Callback Implementation

If you need to maintain brand consistency, set up a proxy callback on your domain that forwards requests to Outstand.

How It Works

  1. TikTok redirects to your callback: https://yourdomain.com/api/socials/tiktok/callback?code=...&state=...
  2. Your proxy endpoint forwards the request to Outstand: https://www.outstand.so/app/api/socials/tiktok/callback
  3. Outstand processes the OAuth flow and redirects back to your application

Implementation Examples

Next.js (App Router)

Create app/api/socials/tiktok/callback/route.ts:

import { NextRequest, NextResponse } from 'next/server';

export async function GET(request: NextRequest) {
	const url = new URL(request.url);
	const outstandCallback = new URL('https://www.outstand.so/app/api/socials/tiktok/callback');
	
	// Forward all query parameters
	url.searchParams.forEach((value, key) => {
		outstandCallback.searchParams.set(key, value);
	});
	
	// Forward cookies from the original request
	const cookies = request.headers.get('Cookie') || '';
	
	try {
		// Forward the request to Outstand's callback
		const response = await fetch(outstandCallback.toString(), {
			method: 'GET',
			headers: {
				'Cookie': cookies,
			},
			redirect: 'manual', // Don't follow redirects automatically
		});
		
		// Get the redirect location from Outstand's response
		const location = response.headers.get('Location');
		
		if (location) {
			// Redirect user to Outstand's final destination
			return NextResponse.redirect(location);
		}
		
		// Fallback: redirect to Outstand callback directly
		return NextResponse.redirect(outstandCallback.toString());
	} catch (error) {
		console.error('Proxy callback error:', error);
		// Fallback: redirect to Outstand callback directly
		return NextResponse.redirect(outstandCallback.toString());
	}
}

Next.js (Pages Router)

Create pages/api/socials/tiktok/callback.ts:

import type { NextApiRequest, NextApiResponse } from 'next';

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
	const { query } = req;
	const outstandCallback = new URL('https://www.outstand.so/app/api/socials/tiktok/callback');
	
	// Forward all query parameters
	Object.entries(query).forEach(([key, value]) => {
		if (typeof value === 'string') {
			outstandCallback.searchParams.set(key, value);
		}
	});
	
	// Forward cookies
	const cookies = req.headers.cookie || '';
	
	try {
		const response = await fetch(outstandCallback.toString(), {
			method: 'GET',
			headers: {
				'Cookie': cookies,
			},
			redirect: 'manual',
		});
		
		const location = response.headers.get('Location');
		
		if (location) {
			return res.redirect(302, location);
		}
		
		return res.redirect(302, outstandCallback.toString());
	} catch (error) {
		console.error('Proxy callback error:', error);
		return res.redirect(302, outstandCallback.toString());
	}
}

Netlify Functions

Create netlify/functions/tiktok-callback.ts:

import type { Handler, HandlerEvent, HandlerContext } from '@netlify/functions';

export const handler: Handler = async (event: HandlerEvent, context: HandlerContext) => {
	const queryParams = new URLSearchParams();
	
	// Forward all query parameters
	Object.entries(event.queryStringParameters || {}).forEach(([key, value]) => {
		if (value) {
			queryParams.set(key, value);
		}
	});
	
	const outstandCallback = `https://www.outstand.so/app/api/socials/tiktok/callback?${queryParams.toString()}`;
	
	// Forward cookies
	const cookies = event.headers.cookie || '';
	
	try {
		const response = await fetch(outstandCallback, {
			method: 'GET',
			headers: {
				'Cookie': cookies,
			},
			redirect: 'manual',
		});
		
		const location = response.headers.get('Location');
		
		return {
			statusCode: 302,
			headers: {
				'Location': location || outstandCallback,
			},
		};
	} catch (error) {
		console.error('Proxy callback error:', error);
		return {
			statusCode: 302,
			headers: {
				'Location': outstandCallback,
			},
		};
	}
};

Add to netlify.toml:

[[redirects]]
  from = "/api/socials/tiktok/callback"
  to = "/.netlify/functions/tiktok-callback"
  status = 200
  force = true

Express.js

const express = require('express');
const router = express.Router();

router.get('/api/socials/tiktok/callback', async (req, res) => {
	const outstandCallback = new URL('https://www.outstand.so/app/api/socials/tiktok/callback');
	
	// Forward all query parameters
	Object.entries(req.query).forEach(([key, value]) => {
		outstandCallback.searchParams.set(key, value);
	});
	
	// Forward cookies
	const cookies = req.headers.cookie || '';
	
	try {
		const response = await fetch(outstandCallback.toString(), {
			method: 'GET',
			headers: {
				'Cookie': cookies,
			},
			redirect: 'manual',
		});
		
		const location = response.headers.get('Location');
		
		if (location) {
			return res.redirect(302, location);
		}
		
		return res.redirect(302, outstandCallback.toString());
	} catch (error) {
		console.error('Proxy callback error:', error);
		return res.redirect(302, outstandCallback.toString());
	}
});

module.exports = router;

Vercel Serverless Function

Create api/socials/tiktok/callback.ts:

import type { VercelRequest, VercelResponse } from '@vercel/node';

export default async function handler(req: VercelRequest, res: VercelResponse) {
	const { query } = req;
	const outstandCallback = new URL('https://www.outstand.so/app/api/socials/tiktok/callback');
	
	// Forward all query parameters
	Object.entries(query).forEach(([key, value]) => {
		if (typeof value === 'string') {
			outstandCallback.searchParams.set(key, value);
		}
	});
	
	// Forward cookies
	const cookies = req.headers.cookie || '';
	
	try {
		const response = await fetch(outstandCallback.toString(), {
			method: 'GET',
			headers: {
				'Cookie': cookies,
			},
			redirect: 'manual',
		});
		
		const location = response.headers.get('Location');
		
		if (location) {
			return res.redirect(302, location);
		}
		
		return res.redirect(302, outstandCallback.toString());
	} catch (error) {
		console.error('Proxy callback error:', error);
		return res.redirect(302, outstandCallback.toString());
	}
}

Step 5: Obtain Your Credentials

Once your app is approved (or in sandbox mode):

  1. Navigate to your app in the TikTok Developer Portal
  2. Go to Basic Information
  3. You'll find:
    • Client Key: Your OAuth client key (public identifier)
    • Client Secret: Your OAuth client secret (keep this secure!)

Security Note: Your Client Secret is sensitive. Store it securely and never expose it in client-side code or public repositories.

Step 6: Add Credentials to Outstand

Once you have your Client Key and Client Secret, add them to Outstand:

Method 1: Using the API

curl -X POST https://api.outstand.so/v1/social-networks \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "network": "tiktok",
    "client_key": "YOUR_CLIENT_KEY_HERE",
    "client_secret": "YOUR_CLIENT_SECRET_HERE"
  }'

Method 2: Using the Dashboard

  1. Log in to Outstand Dashboard
  2. Navigate to SettingsSocial Networks
  3. Select TikTok from the dropdown
  4. Enter your Client Key
  5. Enter your Client Secret
  6. Click Add Social Network

Post Modes

TikTok's Content Posting API offers two ways to deliver a post. Choose with the postMode field in the tiktok block of Create a post.

MEDIA_UPLOAD (default)DIRECT_POST
What happensThe media is delivered to the creator's TikTok inbox as a draft. They write the caption, choose visibility and settings, and publish from the TikTok app.The post is published straight to the creator's profile.
Endpoint/v2/post/publish/inbox/video/init/ (video), /v2/post/publish/content/init/ (photo)/v2/post/publish/video/init/ (video), /v2/post/publish/content/init/ (photo)
Scopevideo.uploadvideo.publish
privacyLevelNot used - the creator picks visibility in the appRequired, and must come from the creator's privacy_level_options
Other tiktok fieldsNot used for video drafts; the caption is kept for photo draftsAll apply
Counts against the daily creator capNoYes
Goes live on scheduleNo - the creator must finish it in the TikTok appYes

MEDIA_UPLOAD posts have no public URL or analytics until the creator publishes them. The stored platformPostId is TikTok's publish_id rather than a video ID, platformPostUrl is null, and post analytics return an error explaining the post is still a draft. TikTok also expires publish records after about 24 hours, after which the draft can no longer be correlated back to the Outstand post.

Media Requirements

TikTok media is uploaded through the Content Posting API. See the official Media Transfer Guide and Direct Post reference.

Video

RequirementSpecification
FormatsMP4 (H.264), MOV, WEBM
Maximum durationDynamic - read max_video_post_duration_sec from the Query Creator Info endpoint before posting
Upload chunkingVideos under 5 MB are uploaded whole; larger videos are chunked (each chunk 5-64 MB, final chunk up to 128 MB, max 1,000 chunks)
Transfer methodPULL_FROM_URL - Outstand gives TikTok the media URL and TikTok fetches it, so the URL must stay reachable until publishing completes
Upload URL validityThe issued upload_url is valid for 1 hour

TikTok enforces the maximum video duration per creator - always fetch the current value from max_video_post_duration_sec rather than assuming a fixed number.

Photo Mode

RequirementSpecification
FormatsWebP, JPEG
Number of photos1 to 35 images
DeliveryProvided as publicly accessible image URLs

See the Photo Post reference.

Publishing Rate Limits

TikTok rate-limits the Content Posting API per user access token on a 1-minute sliding window. See the official rate limit documentation.

EndpointLimit
Post init (/v2/post/publish/...)6 requests per minute per user
Status / fetch endpoints30 requests per minute per user
Direct Post active creator capRolling 24 hours, per API client - see below
  • Exceeding a limit returns HTTP 429 with a rate_limit_exceeded error.
  • TikTok also applies per-creator daily posting limits that are enforced platform-side; these are not published as a fixed API number and are shared across all apps posting for that creator.
  • Separately, TikTok caps how many distinct creators may publish through one API client in a rolling 24 hours. Unaudited clients are limited to 5; audited clients get a cap based on the usage estimates in their audit application. Only DIRECT_POST draws on this cap - MEDIA_UPLOAD does not. Exceeding it returns reached_active_user_cap (see Publishing errors).

Comments and Replies

Comments and replies are not supported for TikTok.

As a result, the following are not available for TikTok accounts:

  • Publish a comment (POST /v1/posts/{id}/replies) - returns an error for TikTok accounts.
  • Get post replies/comments (GET /v1/posts/{id}/replies) - returns an error for TikTok accounts.
  • First comment via the containers field - the reply container cannot be delivered to TikTok.

What TikTok does expose, and Outstand supports:

  • Disabling comments at publish time. TikTok's Content Posting API accepts a per-post disable_comment toggle, so you can turn comments off on a post (or photo) when you publish it.
  • Comment counts in analytics. The read-only comment_count metric is included in post analytics and account insights.

Troubleshooting

OAuth Flow Fails

  • Check redirect URI: Ensure it exactly matches what's configured in TikTok (including trailing slashes)
  • Verify scopes: All 6 scopes must be enabled in your TikTok app
  • Check sandbox: If in sandbox mode, ensure test user is added

App Rejected by TikTok

  • Brand inconsistency: Use proxy callback solution to match redirect URI to your domain
  • Missing scope demo: Update demo video to show all enabled scopes
  • Invalid website: Ensure website URL is accessible and uses HTTPS

Scope Errors

  • Missing scopes: Enable all 6 scopes in TikTok app configuration
  • Scope mismatch: Ensure TikTok app scopes match what Outstand requests

Publishing errors

These are TikTok error codes returned when a post is published. Outstand surfaces them on the failed social account with guidance attached.

CodeWhat it meansWhat to do
reached_active_user_capThe app has already published for as many distinct creators as TikTok allows in a rolling 24 hours. Unaudited clients get 5; audited clients get a cap set from their audit application. The cap belongs to the TikTok app, so every account sharing that app draws on the same pool.Publish with postMode: "MEDIA_UPLOAD", which is not subject to the cap; or connect your own TikTok app credentials so you get your own cap; or wait for the 24-hour window to roll over. To raise the cap, get the app audited and then request an increase through TikTok's developer support form - the app must be in production, and increases are not guaranteed. See Direct Post Active Creator Cap.
unaudited_client_can_only_post_to_private_accountsThe TikTok app has not passed the Content Posting API audit, so Direct Posts are forced to private.Set privacyLevel to SELF_ONLY, use MEDIA_UPLOAD, or submit the app for audit.
spam_risk_too_many_postsThe creator hit TikTok's per-account daily posting limit.Reduce posting frequency for that creator and retry later.
spam_risk_user_banned_from_postingTikTok has blocked the creator from posting.The creator needs to contact TikTok support - this cannot be resolved from Outstand.
rate_limit_exceededMore than 6 publish requests in a minute for that creator.Retry after a minute.
url_ownership_unverifiedThe media URL's domain is not verified for the TikTok app.Verify the domain under URL Properties in the TikTok developer portal.
privacy_level_option_mismatchThe chosen privacyLevel is not one the creator is allowed to use.Re-fetch the creator's privacy_level_options and pick one of them.
access_token_invalidThe connection expired or was revoked.Reconnect the TikTok account.
scope_not_authorizedThe connection is missing video.publish (Direct Post) or video.upload (inbox drafts).Reconnect the account and approve every requested scope.