# Run a faceless TikTok channel with Mastra

Source: https://developers.tokportal.com/agents/mastra/faceless-channel/
Markdown: https://developers.tokportal.com/agents/mastra/faceless-channel.md

A faceless channel from Mastra is a pipeline: your generator (Sora, Veo, Runway, ElevenLabs + stock footage — whatever you use) produces clips, the agent uploads them with `upload_video`, fills dated slots with `batch_configure_bundle_videos` (up to 3 per day per bundle, `ai_content_disclaimer: true` where it applies) and `publish_bundle`; a TokPortal manager posts them from a real, geo-targeted account and `get_account_analytics` tells you what worked. No OAuth per channel, no app audit, and the account is warmed and held by a human, which is what faceless projects usually lack.

## Connect TokPortal to Mastra

Mastra is an agent framework (code): TypeScript agent framework; `MCPClient` from `@mastra/mcp` connects HTTP or stdio MCP servers and hands their tools to an `Agent`.

```bash
npm i @mastra/core @mastra/mcp
```

```ts
import { Agent } from '@mastra/core/agent';
import { MCPClient } from '@mastra/mcp';

const mcp = new MCPClient({
  servers: {
    tokportal: {
      url: new URL('https://app.tokportal.com/api/ext/mcp'),
      requestInit: {
        headers: { 'X-API-Key': process.env.TOKPORTAL_API_KEY! },
      },
    },
  },
  timeout: 30000,
});

export const socialOps = new Agent({
  id: 'social-ops',
  name: 'Social ops',
  instructions: 'You operate TikTok and Instagram accounts through TokPortal tools. Quote credit costs before creating bundles.',
  model: 'anthropic/claude-sonnet-4-5',
  tools: await mcp.listTools(),
});
```

Tools are namespaced `tokportal_tokportal_*` (server name + tool name). Use `mcp.listToolsets()` to pass tools per request instead of at construction, and `mcp.disconnect()` on shutdown.

Snippet status: **verified** against Mastra's documentation ([source](https://mastra.ai/en/reference/tools/mcp-client)).

Once connected, sanity-check the setup with a read-only call: "What is my TokPortal credit balance?" should trigger `tokportal_get_credit_balance`. GET requests and dry-run writes do not spend credits.

## Try the workflow without spending

For this trial, use the remote MCP endpoint `https://app.tokportal.com/api/ext/mcp`, local `tokportal-mcp` **1.15.1 or later**, or the REST API. Upgrade and restart an older local server first. Version 1.15.0 does not forward the dry-run header; do not use its write tools for a simulation. If you cannot verify the installed local version, use the remote endpoint or REST.

Use the [sandbox](https://developers.tokportal.com/sandbox) for the first attempt. MCP write tools accept `dry_run: true` as a top-level argument, beside `body`; REST writes accept the `X-TokPortal-Dry-Run: true` header. Keep your usual authentication and scopes. Each simulated write validates the request and returns `credits_would_charge` with `credits_charged: 0`. Nothing is created or published.

Simulated IDs start with `00000000-0000-4000-8000-`. Use them only in later dry-run writes. GET requests read real data, so skip publish-readiness checks and polling for synthetic IDs. Simulations have no cross-call memory and upload URLs are placeholders: do not upload files to them.

To run for real, review the returned price and confirm **before the first live write**. Bundle creation charges credits immediately; publishing hands the order to a manager. Repeat creation without dry-run mode and use the new real IDs for configuration and publishing. A synthetic ID cannot be reused in a live request.

## How it works

The economics of faceless channels are volume and consistency, so the design starts with the 3-videos-per-day-per-bundle cap: one channel is one account, one account posts at most 3 times a day, and a 30-day content plan is 90 slots (`videos_quantity: 90`). If you run five channels, run five bundles; `create_bundles_bulk` creates them in one call, each becoming its own account in the chosen country.

Per clip the agent needs a file URL, a caption and a date. `upload_video` gives a presigned PUT target and a `public_url` — from a script the agent uploads directly; from a chat host it can pass any public URL (your storage bucket, a Drive/R2 link) straight into `video_url`. `batch_configure_bundle_videos` takes the whole month at once; each item carries `position`, `video_type: "video"`, `description` (caption + hashtags, ≤2200 chars), `target_publish_date`, `video_url`, and optionally `tiktok_sound_url` for a trending sound, `ai_content_disclaimer: true` (free — the manager toggles TikTok's AI-generated label) and `editing_instructions` if you want light edits (edit slots cost 3 credits). `disclose_as_ads` covers sponsored clips.

After `publish_bundle`, videos progress to `finalized` (auto after 72 h in review unless you disable `auto_finalize_videos`), and the agent closes the loop with `list_account_video_analytics` (per-video views/likes/comments) and `get_analytics_series` (daily followers/views), then adjusts the next batch. A weekly cron in Mastra — "pull last week's analytics, rank hooks, generate 21 new clips, fill next week's slots" — is the whole operating model.

## Run it from Mastra

With Mastra the sequence is driven by your code: give the agent the tools from the TokPortal MCP server and the prompt below as the user message (or split it into tasks). Log every `tokportal_*` call and its arguments in development — the tool list is large (91 tools), so consider filtering to the ones this job needs.

**Tool sequence**

1. `tokportal_create_bundle` — `account_and_videos`, `platform: tiktok`, `country`, `videos_quantity: 90` for a month; optional warming terms in the channel's niche.
2. `tokportal_configure_bundle_account` — Channel handle, display name, bio and a `profile_picture_url` (upload with `upload_image_from_url`).
3. `tokportal_upload_video` — Presigned upload per generated clip → `public_url`.
4. `tokportal_batch_configure_bundle_videos` — Whole month in one call; `ai_content_disclaimer: true`; 3 per day.
5. `tokportal_publish_bundle` — Manager creates the account, warms if ordered, then posts by date.
6. `tokportal_list_account_video_analytics` — Per-video performance to rank hooks and formats.
7. `tokportal_get_analytics_series` — `metric=followers|views`, `granularity=day` for the channel curve.
8. `tokportal_add_video_slots` — Extend the same account with `quantity` more slots next month.

**Prompt to paste**

```text
Evaluate this workflow in the TokPortal sandbox only.
Use the remote MCP endpoint https://app.tokportal.com/api/ext/mcp or local tokportal-mcp 1.15.1 or later for write tools.
Verify the installed local version before using its write tools. Version 1.15.0 is unsafe for simulations.
If the local version is older or unknown, use the remote endpoint or authenticated REST
requests with X-TokPortal-Dry-Run: true instead.
For every non-GET MCP tool call, set dry_run: true as a top-level boolean beside body.
For every REST write in a workflow, send the X-TokPortal-Dry-Run: true header.
Use normal read tools for existing real objects. Do not GET or poll synthetic IDs,
and do not upload files to simulated upload URLs. Simulated objects have no cross-call memory.
Show credits_would_charge and verify credits_charged is 0 for each simulated write.
Do not execute any live write. Ask for my explicit GO before the first real write,
including bundle creation, which charges immediately. After GO, repeat creation
without dry_run and use the new real IDs; never reuse synthetic IDs in live requests.

Use the TokPortal MCP tools. I run a faceless "history shorts" channel; clips are at
https://cdn.example.com/history/clip-001.mp4 … clip-021.mp4 with captions in captions.csv.
Create a TikTok bundle in the US (account_and_videos, 21 videos), configure @rome.in.45s,
fill positions 1–21 with batch_configure_bundle_videos (3 per day from 2026-09-01, ai_content_disclaimer true),
simulate publish_bundle and report validation and credits_would_charge.
```

Run with `mastra dev` and test in the playground, or call `agent.generate()` from your server code.

**REST equivalent** (write requests below are simulations; GET requests read existing real data)

```bash
# Channel account + 90 slots
curl -X POST -H "X-TokPortal-Dry-Run: true" https://app.tokportal.com/api/ext/bundles \
  -H "X-API-Key: sk_..." -H "Content-Type: application/json" -H "Idempotency-Key: $(uuidgen)" \
  -d '{"bundle_type":"account_and_videos","platform":"tiktok","country":"US","videos_quantity":90,"title":"History shorts"}'

# Presigned upload for one clip
curl -X POST -H "X-TokPortal-Dry-Run: true" https://app.tokportal.com/api/ext/upload/video \
  -H "X-API-Key: sk_..." -H "Content-Type: application/json" \
  -d '{"filename":"clip-001.mp4","content_type":"video/mp4","bundle_id":"BUNDLE_ID"}'
# The dry-run upload URL is a placeholder. Do not PUT a file to it.
# Use a public sample video URL when simulating slot configuration.

# Batch three per day, AI label on
curl -X PUT -H "X-TokPortal-Dry-Run: true" https://app.tokportal.com/api/ext/bundles/BUNDLE_ID/videos/batch \
  -H "X-API-Key: sk_..." -H "Content-Type: application/json" \
  -d '{"videos":[{"position":1,"video_type":"video","description":"Why Rome fell in 45 seconds #history","target_publish_date":"2026-09-01","video_url":"https://.../clip-001.mp4","ai_content_disclaimer":true}]}'
```

## Key parameters

| Parameter               | Values                 | Notes                                                          |
| ----------------------- | ---------------------- | -------------------------------------------------------------- |
| `videos_quantity`       | 0–500                  | 90 = one month at 3/day.                                       |
| `ai_content_disclaimer` | boolean (free)         | Manager enables TikTok's AI-generated content label.           |
| `description`           | ≤2200 chars            | Caption + hashtags; required for video/carousel.               |
| `tiktok_sound_url`      | URI                    | Trending sound; volume\_added\_sound 0–200 (1 credit).         |
| `editing_instructions`  | ≤5000 chars            | Light edits by the manager; needs edit slots (3 credits each). |
| `auto_finalize_videos`  | boolean (default true) | Keep true for hands-off channels.                              |

Full schemas: [OpenAPI reference](https://developers.tokportal.com/api-reference) · [openapi.json](https://developers.tokportal.com/openapi.json).

## Example configurations

**Channel with niche warming**

```json
{"bundle_type":"account_and_videos","platform":"tiktok","country":"US","videos_quantity":90,
 "wants_advanced_warming":true,"advanced_warming_terms":["history facts","ancient rome","ww2 documentary"]}
```

**Batch item for an AI clip**

```json
{"position":12,"video_type":"video","description":"The pirate who stole a whole navy 🏴‍☠️ #history #storytime",
 "target_publish_date":"2026-09-04","video_url":"https://pub-xxx.r2.dev/videos/b/012.mp4","ai_content_disclaimer":true}
```

**Extend next month**

```json
{"quantity":90}
```

## Credits

One channel month ≈ 32 (account) + 90 × 2 (slots) = 212 credits at standard rates, plus 5 per warming target and 3 per edit slot if used. Subsequent months are slots only (`add_video_slots`, 2 credits each). Live rates via `get_credit_costs` — see [Credits & Pricing](https://developers.tokportal.com/credits).

## Why not the official API

Faceless operators hit every wall of the official route at once: each channel needs its own OAuth grant, posts stay private until the app is audited, the per-token 6 requests/minute limit and the undocumented daily cap (`spam_risk_too_many_posts`) throttle multi-channel schedules, and the account still has to be created and warmed by hand. TokPortal collapses that into one API key, human-operated accounts and dated slots.

As of August 2026 the first-party routes look like this. TikTok's Content Posting API (Direct Post) requires each account owner to authorize your app with the `video.publish` scope, limits every user access token to 6 requests per minute, keeps all posts from unaudited apps in private viewing mode until TikTok audits the app, and enforces an unpublished daily post cap per user (`spam_risk_too_many_posts`) that integrators commonly report at roughly 15–25 posts per account per day. Meta's Instagram Content Publishing API allows 100 API-published posts per professional account in a 24-hour moving window and only for accounts you own and connect via OAuth. Neither creates accounts, warms them, or reports third-party bans. TokPortal is human-operated infrastructure: accounts created and run by managers in the target country, one `X-API-Key` for all of them, dated slots (max 3 per day per bundle) instead of per-account tokens.

Sources: [TikTok Content Posting API – Direct Post](https://developers.tiktok.com/doc/content-posting-api-reference-direct-post), [TikTok Content Posting API – Get started](https://developers.tiktok.com/doc/content-posting-api-get-started), [Instagram Platform – Content Publishing](https://developers.facebook.com/docs/instagram-platform/content-publishing).

## FAQ

### Can the agent upload straight from a generation API?

Yes — most generators return a temporary URL; the agent fetches it and PUTs to the presigned `upload_video` URL (or, in hosts without file access, passes the public URL directly as `video_url` if it stays valid until posting).

### Should I always set ai\_content\_disclaimer?

If the clip is AI-generated, yes: it is free, it is TikTok's rule, and it protects the account. Set `disclose_as_ads` too when the clip is sponsored.

### How do I run several channels in different niches?

One bundle per channel; use `external_ref` (e.g. `history-us-01`) so your agent can `list_bundles` by reference, and different warming terms per bundle.

### What if a clip fails TikTok's rules?

The manager flags it (`video.pending_corrections` with a comment); the agent replaces `video_url`/caption with `configure_bundle_video` and it re-enters the queue.

## Related

- [Media upload](https://developers.tokportal.com/media-upload)
- [Configure videos](https://developers.tokportal.com/configure-videos)
- [Analytics API](https://developers.tokportal.com/analytics)
- [UGC distribution at scale](https://developers.tokportal.com/use-cases/industry/ugc-distribution-at-scale)
- [MCP server](https://developers.tokportal.com/mcp)

## Also works with

- [Run a faceless TikTok channel with Microsoft Copilot Studio](https://developers.tokportal.com/agents/copilot-studio/faceless-channel)
- [Run a faceless TikTok channel with Factory Droid](https://developers.tokportal.com/agents/factory-droid/faceless-channel)
- [Run a faceless TikTok channel with n8n](https://developers.tokportal.com/agents/n8n/faceless-channel)
- [Run a faceless TikTok channel with Make](https://developers.tokportal.com/agents/make/faceless-channel)
- [Run a faceless TikTok channel with Zapier](https://developers.tokportal.com/agents/zapier/faceless-channel)
- [Run a faceless TikTok channel with Pipedream](https://developers.tokportal.com/agents/pipedream/faceless-channel)

[All 25 agents for "Run a faceless TikTok channel"](https://developers.tokportal.com/agents/for/faceless-channel)

## Other jobs with Mastra

- [Create TikTok accounts with Mastra](https://developers.tokportal.com/agents/mastra/create-tiktok-accounts)
- [Post 100 TikTok videos a day with Mastra](https://developers.tokportal.com/agents/mastra/post-100-videos-a-day)
- [Run US TikTok accounts from abroad with Mastra](https://developers.tokportal.com/agents/mastra/us-tiktok-accounts-from-abroad)
- [Warm TikTok & Instagram accounts with Mastra](https://developers.tokportal.com/agents/mastra/warm-accounts)
- [Seed UGC across many accounts with Mastra](https://developers.tokportal.com/agents/mastra/ugc-seeding)
- [Seed a music sound on TikTok with Mastra](https://developers.tokportal.com/agents/mastra/music-sound-seeding)
- [Launch an app with persona accounts with Mastra](https://developers.tokportal.com/agents/mastra/app-persona-launch)
- [Run multi-client agency operations with Mastra](https://developers.tokportal.com/agents/mastra/agency-multi-client)
- [Get ban alerts via webhooks with Mastra](https://developers.tokportal.com/agents/mastra/ban-webhooks)
- [Pull analytics across all accounts with Mastra](https://developers.tokportal.com/agents/mastra/analytics-multi-account)
- [Create Instagram accounts with Mastra](https://developers.tokportal.com/agents/mastra/create-instagram-accounts)
