---
name: agentic-posting
description: "Publish videos or photo slideshows to TikTok through Agentic Posting. Use for TikTok draft delivery, browser-reviewed posts, or separately confirmed Direct Post."
---

# Agentic Posting

Agentic Posting is TikTok-only. It provides three operations:

- `upload_tiktok_draft`: ask TikTok to deliver media to the creator's inbox so the creator can finish the post in TikTok.
- `prepare_tiktok_post`: prepare an immutable post and return a complete summary plus an expiring review URL.
- `publish_tiktok_post`: confirm a prepared post only after a separate, affirmative response from the creator.

Never treat an earlier instruction such as "post automatically" as consent for a specific Direct Post. Never prepare and confirm a Direct Post in the same conversational turn. Never confirm in the same user turn. Never generate the confirmation response yourself.

## Setup

The user must first sign in, subscribe, connect one TikTok account, and claim an API key at `https://agenticposting.com/dashboard.html`.

Store the key outside source control:

```bash
export AGENTIC_POSTING_API_KEY="agp_..."
```

Resolve the workspace and connected-account ids instead of asking the user to copy them:

```bash
curl -fsS https://api.agenticposting.com/v1/context \
  -H "x-api-key: $AGENTIC_POSTING_API_KEY"
```

If `nextAction.type` is not `ready`, stop and send the user to the returned dashboard URL. Do not attempt publishing without an active subscription and a connected TikTok account.

Check `tiktokAccounts[].scopes` in the context response before choosing a workflow. Draft/inbox delivery requires `video.upload`; Direct Post requires `video.publish`. If the needed scope is missing, send the creator to the dashboard to reconnect TikTok. Do not probe a missing grant by attempting a publish.

Machine-readable contracts:

- Capabilities: `https://api.agenticposting.com/v1/capabilities`
- OpenAPI: `https://api.agenticposting.com/v1/openapi.json`
- Human docs: `https://agenticposting.com/docs.html`

## Upload media

Each reservation must contain exactly one MP4 video or between 1 and 35 JPEG/WebP photos; never mix video and photo files in one reservation. The launch limit is 50 MB for the video and 20 MB per photo. A workspace may have at most 70 unexpired active assets and 1,000,000,000 active bytes across pending, uploading, and ready media. Failed or expired assets stop consuming that capacity. Media is temporary and normally deleted after 24 hours. Compute each file's SHA-256 digest and send it as exactly 64 lowercase hexadecimal characters.

```bash
curl -fsS -X POST https://api.agenticposting.com/v1/uploads \
  -H "x-api-key: $AGENTIC_POSTING_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "workspaceId":"wsp_123",
    "files":[{
      "fileName":"clip.mp4",
      "contentType":"video/mp4",
      "sizeBytes":12345678,
      "mediaRole":"video",
      "sha256":"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
    }]
  }'
```

Replace the example digest with the lowercase output of `sha256sum clip.mp4 | awk '{print $1}'`, or `shasum -a 256 clip.mp4 | awk '{print $1}'` on macOS.

Upload each file exactly once using the returned `uploadUrl`, exact reserved `Content-Type`, and the same byte length declared in the reservation:

```bash
curl -fsS -X PUT "$UPLOAD_URL" \
  -H "x-api-key: $AGENTIC_POSTING_API_KEY" \
  -H "Content-Type: video/mp4" \
  -H "Content-Length: $UPLOAD_SIZE_BYTES" \
  --data-binary @clip.mp4
```

Preserve the caller's `assetIds` order for photo slideshows. Each `assetId` may appear only once in a draft or post intent. A supplied zero-based `photoCoverIndex` must identify one of those ordered photo assets; omitted photo input becomes index `0`.

## Operation: upload_tiktok_draft

Use this when the user wants to finish caption, privacy, disclosures, and publishing inside TikTok, or when Direct Post confirmation cannot be displayed safely.

Always use this draft path for an AI-generated photo slideshow. TikTok's photo Direct Post request does not expose an AI-generated-content disclosure field, so Agentic Posting does not Direct Post AIGC photos at launch. The creator must review and set the disclosure in TikTok.

```bash
curl -fsS -X POST https://api.agenticposting.com/v1/posts \
  -H "x-api-key: $AGENTIC_POSTING_API_KEY" \
  -H "Idempotency-Key: draft-unique-id" \
  -H "Content-Type: application/json" \
  -d '{
    "workspaceId":"wsp_123",
    "accountId":"ttk_123",
    "mode":"draft",
    "mediaType":"video",
    "assetIds":["ast_123"]
  }'
```

Inspect the exact returned stored job before describing the outcome. Only a non-null `publishId` is durable proof that TikTok accepted this initialization. Then tell the user TikTok accepted the inbox-share request, that acceptance is not delivery, and that they must wait for TikTok's inbox notification before finishing the post there. An idempotent HTTP 200 can instead recover a stored `failed` or `submission_unknown` job; report that exact state without claiming acceptance and never create a replacement. TikTok independently limits pending inbox shares; the 300-request plan does not override that platform limit.

## Operation: prepare_tiktok_post

Direct Post always begins by creating an intent. Interactions should be `false` unless the user explicitly chose otherwise. `privacyLevel` and `isAigc` must be explicit choices; do not infer them silently.

For video, send the TikTok caption in `title` (up to 2,200 characters) and omit `description`; video intents containing `description` are rejected because TikTok Direct does not send that field. If the creator supplied meaningful text in both fields, ask them to choose or approve the single final caption. Never silently drop, merge, or rewrite either value. Video may use `allowDuet`, `allowStitch`, and `videoCoverTimestampMs`; omit the photo-only `autoAddMusic` and `photoCoverIndex` properties entirely, even when false. For photo, `title` is the short title (up to 90 characters) and `description` is separate (up to 4,000 characters); show both when supplied. Photo may use `autoAddMusic` and `photoCoverIndex`, must set `allowDuet` and `allowStitch` to false, and must omit the video-only `videoCoverTimestampMs` property. An omitted photo `photoCoverIndex` is normalized to `0` before hashing, storage, review, and submission. The API rejects media-incompatible settings rather than silently dropping them. Set `commercialContentDisclosure` to exactly `brandContentToggle || brandOrganicToggle`. Treat “Your brand” (`brandOrganicToggle`) and “Branded content” (`brandContentToggle`) as independent creator choices.

Do not prepare a Direct Post intent for an AI-generated photo slideshow. Use `upload_tiktok_draft` instead.

Use `BROWSER_REVIEW` unless `GET /v1/capabilities` says `publish_tiktok_post.available` is true and the current agent can show every preview and receive a separate user response.

```bash
curl -fsS -X POST https://api.agenticposting.com/v1/post-intents \
  -H "x-api-key: $AGENTIC_POSTING_API_KEY" \
  -H "Idempotency-Key: intent-unique-id" \
  -H "Content-Type: application/json" \
  -d '{
    "path":"BROWSER_REVIEW",
    "accountId":"ttk_123",
    "mediaType":"video",
    "assetIds":["ast_123"],
    "title":"Complete caption and hashtags",
    "privacyLevel":"SELF_ONLY",
    "allowComment":false,
    "allowDuet":false,
    "allowStitch":false,
    "isAigc":true,
    "commercialContentDisclosure":false,
    "brandContentToggle":false,
    "brandOrganicToggle":false
  }'
```

The response contains `postIntentId`, `expiresAt`, `reviewUrl`, `contentHash`, `confirmationPhrase`, media previews, and a summary.

For browser review, give the creator the exact `reviewUrl`. New links contain an opaque `agp_review_` capability; legacy bare digest tokens are invalid. The creator must open the link and sign in to Agentic Posting as a member of the workspace that prepared the intent. The review token in the URL is sufficient to read an active review, but it cannot confirm by itself. Browser confirmation requires that token and the creator's current Clerk session together. Never try to select `confirmationChannel: "browser"` with an API key, and never ask the creator to paste a session or review token into chat.

Before any agent-channel confirmation, show the creator all of the following without truncation:

1. Connected creator identity.
2. Every media preview in order.
3. Every submitted text field: video caption in `title`, or the photo `title` and `description` independently.
4. Privacy, Comments, Duet, and Stitch.
5. AI-generated-content choice.
6. Auto-add music, photo cover index, and video cover timestamp exactly as returned, including an explicit not-applicable value for the other media type. A photo always has an effective cover index; omitted input becomes `0`.
7. Commercial-content disclosure, Your brand, and Branded content as three explicit fields; never collapse or hide a true toggle.
8. TikTok's Music Usage Confirmation and, when applicable, Branded Content Policy declaration.
9. The exact confirmation phrase and expiry time.
10. Notice that TikTok processing and moderation occur after acceptance.

Then stop. Ask the user to reply with the exact phrase if they approve this exact immutable summary. Wait for a new user message; that separate response is required.

If anything changes, do not confirm the old intent. Prepare a new intent with a new `Idempotency-Key`.

## Operation: publish_tiktok_post

Call this only after a new user message explicitly approves the exact prepared summary. Copy `confirmationPhrase` and `contentHash` from the prepare response; do not manufacture either value.

```bash
curl -fsS -X POST https://api.agenticposting.com/v1/post-intents/pi_123/confirm \
  -H "x-api-key: $AGENTIC_POSTING_API_KEY" \
  -H "Idempotency-Key: confirm-unique-id" \
  -H "Content-Type: application/json" \
  -d '{
    "approved":true,
    "confirmationPhrase":"PUBLISH 7K9F",
    "contentHash":"sha256:replace_with_prepare_response",
    "confirmationChannel":"agent"
  }'
```

If the API returns `agent_direct_post_not_enabled`, open or give the user the `reviewUrl`. Do not work around the feature gate.

An intent expires after at most ten minutes and can be consumed once. Wrong phrases, changed hashes, expired intents, and repeat confirmation attempts must not trigger another post.

## Poll status

```bash
curl -fsS -X POST "https://api.agenticposting.com/v1/posts/job_123/refresh?workspaceId=wsp_123" \
  -H "x-api-key: $AGENTIC_POSTING_API_KEY"
```

Do not claim publication or inbox delivery from the initialization response. Say TikTok accepted the request only when the returned or recovered job has a non-null `publishId`; otherwise report its exact stored state without inferring acceptance. Report `publish_complete`, `send_to_user_inbox`, or an actionable failure only after status refresh or webhook processing proves that stored job state.

## Error recovery

- `invalid_api_key`: direct the user to the dashboard to claim or rotate a key. Never ask them to paste it into chat.
- `payment_required`, `billing_period_expired`, `quota_exceeded`: send the user to dashboard billing. Past-due subscriptions should use the Polar portal, not a second checkout.
- `tiktok_reauthorization_required`: ask the user to reconnect TikTok in the dashboard.
- `tiktok_creator_identity_missing`: stop; no confirmable intent was created. Ask the creator to reconnect TikTok if needed and retry later. Never confirm a summary without a recognizable TikTok username or nickname.
- `review_workspace_forbidden`: ask the creator to sign in with a member account for the workspace that prepared the intent. Never ask for their session or review token.
- `assets_not_ready`, `intent_media_expired`: upload again, then prepare a new intent.
- `mixed_media_reservation`, `duplicate_asset_ids`: correct the reservation or asset list; do not retry the same invalid shape.
- `idempotency_key_reused`, `confirmation_idempotency_conflict`: stop. The key is bound to different input; never change a request under an existing key. Use a new key only for a deliberate new operation after the old job is fully reconciled, never to replace an ambiguous attempt.
- `media_capacity_exceeded`: wait for active upload slots to expire or finish cleanup before reserving more media; do not loop.
- `privacy_level_not_allowed`, interaction errors, or disclosure errors: show TikTok's current allowed values and ask the user to choose again.
- `post_intent_expired`, `confirmation_mismatch`: prepare a new intent. Never reuse consent.
- An existing intent in `submitting`: treat it as already claimed and in flight. Do not confirm it again or prepare a replacement; poll that intent and its returned job, or report that internal recovery is required if no job identity appears.
- `rate_limit_exceeded` or TikTok daily/pending-share limits: show the reset guidance and wait; do not loop aggressively.
- Any ambiguous provider/network result: never create a replacement operation or new idempotency key. For a draft response that did not return a `jobId`, repeat the identical `POST /v1/posts` once with the same `Idempotency-Key` to recover the existing job, then poll it. For Direct Post, read the existing intent and poll its returned `jobId`. If no job or TikTok `publish_id` is exposed, stop and report that the capacity hold requires internal provider/operator evidence. There is no public reconciliation, release, or expiry endpoint; neither the agent nor creator can clear the hold.
- Exact replay remains the recovery path after subscription entitlement ends and never starts a second TikTok publish initialization or reserves a second usage unit. A concurrent racing retry may repeat a safe creator-info metadata read before it observes the winning attempt; this is not a replacement publish.

## Product limits

- One TikTok account.
- 300 TikTok-accepted Direct Post or inbox-share initializations per Polar billing period, assigned to the period containing TikTok acceptance rather than upload, preparation, or reservation time.
- `used` is accepted usage in the current period. `reserved` is unresolved provider-attempt capacity, including holds carried across a period boundary. `remaining` subtracts both until internal provider/operator evidence reconciles the hold; no elapsed-time rule releases it.
- When `remaining` is zero only because of `reserved`, uploads and intent preparation may still proceed because they do not consume quota, but do not initialize a draft or confirm Direct Post until the existing hold is reconciled.
- No scheduling, calendar, multi-network publishing, content generation, or customer webhook delivery.
- Job status polling reports TikTok publishing state; Agentic Posting does not expose launch analytics.
