Comments – Overview & Lifecycle
Programmatically schedule comments from your delivered TokPortal accounts on third-party social videos. Pricing, lifecycle, and error handling.
Comments
Schedule comments from your delivered TokPortal accounts on third-party social videos. Comments are posted by the account manager assigned to each account, then auto-verified by scraping the target video's comment list.
Each task is either a video target (post a comment under a video) or a comment target (reply to a specific comment under a video — TikTok only). Set this with target_type; see Create → Target types.
POST /api/ext/comments Create one or many comment tasks
GET /api/ext/comments List your comment tasks (paginated)
GET /api/ext/comments/{id} Read one task with full state
DELETE /api/ext/comments/{id} Cancel a pending task (exact stored-cost refund)
POST /api/ext/comments/{id}/approve Approve a manually-confirmed task
POST /api/ext/comments/{id}/dispute Dispute a manually-confirmed task
GET /api/ext/comments/{id}/verifications Verbose verifier timeline
Pricing
1 credit per new comment task. In a batch, TokPortal validates every row first. The debit for accepted rows and all accepted task inserts then commit in one database transaction. Rejected rows are never charged. This prevents a task from existing without its matching debit, or a debit from existing without its matching task.
The task's exact stored cost is refunded atomically if:
- you cancel a
pendingtask viaDELETE /api/ext/comments/{id} - the 72-hour hard deadline expires before the manager posts (auto-cancellation)
Current tasks refund 1 credit. Historical tasks can have a different stored cost, so always read credits_refunded from the response instead of assuming an amount.
The CM payout is paid out of TokPortal funds — it does not affect your credit balance.
TokPortal Coverage requirement
The saved account must either have active TokPortal Coverage or be permanently grandfathered. It must also belong to the caller and have an assigned account manager. A single-task request that cannot execute returns MANAGED_ACCOUNT_TASK_BLOCKED (409). A batch keeps partial-success semantics and places the stable account or Coverage reason in rejected[*].reason.
If Coverage later pauses or lapses, the task remains visible to the client but is removed from the manager's executable queue. Its durable workflow status is preserved. Read execution_blocked, execution_blocked_at, and execution_block_reason, then fetch GET /api/ext/accounts/{saved_account_id}/managed-subscription for the authoritative state and reactivation quote. Cancellation of a still-pending task remains available while Coverage is paused.
Supported platforms
| Platform | Comment posting | Reply-to-comment target | Auto-verification | Comment max length |
|---|---|---|---|---|
| TikTok | ✅ | ✅ | ✅ | 150 chars |
| ✅ | ❌ | ✅ | 2,200 chars | |
| YouTube | ❌ (coming) | ❌ | ❌ | — |
Auto-verification scrapes up to 100 of the target video's most recent comments and matches them by author handle + text similarity (Sørensen-Dice ≥ 0.95). For a comment target, the verifier additionally pulls replies and matches the manager's reply the same way (by handle + text) — it does not require the reply to sit under a specific parent comment.
Lifecycle
A task moves through these states. Statuses surface verbatim in GET /api/ext/comments/{id} so your code can branch on them.
| Status | Meaning | Visible in API |
|---|---|---|
pending | Awaiting the account manager to post the comment. Hard deadline: 72h. | ✅ |
submitted | Manager declared they posted. Auto-verifier ran with a 30s grace window — still not matched. The CM should either retry (if a propagation lag) or manual-confirm. | ✅ |
verified_auto | Auto-verifier matched the comment on the target video. Payout locked in 24h (auto-finalize). | ✅ |
manually_confirmed | Auto-verification couldn't find the comment; manager swore they posted. You have 72 hours to approve or dispute. After that, payout auto-locks. | ✅ |
pending_corrections | The manager is fixing something you disputed. Hidden if it was an auto-detected issue (the manager is fixing it before you ever see it). | ✅ (only client-disputed) |
finalized | Payout was locked, manager wallet credited. Terminal state. | ✅ |
cancelled | Cancelled by you (DELETE) or by the 72h expiry sweep. Credit refunded. | ✅ |
rejected / needs_review | Internal — should never appear via the API. | hidden |
Lifecycle diagram (happy path)
create
│
▼
pending ─── manager posts ───► submitted
│
┌─── verifier matches ▼ ────┐
│ │
▼ ▼
verified_auto manually_confirmed
│ │
│ (24h auto-finalize) │ you approve ───► finalized
▼ │ you dispute ───► pending_corrections
finalized │ 72h pass ───► finalized (auto)
▼
─ pending_corrections
│ manager fixes + retries verify
▼
verified_auto / manually_confirmed
Error handling
All endpoints return a uniform error envelope:
{
"error": {
"code": "COMMENT_PLATFORM_MISMATCH",
"message": "The video URL's platform does not match the account's platform...",
"details": { "account_platform": "tiktok", "url_platform": "instagram" }
}
}
Comment-specific error codes
| Code | HTTP | Trigger |
|---|---|---|
COMMENT_TASK_NOT_FOUND | 404 | Task ID doesn't exist or doesn't belong to your account. |
COMMENT_TASK_NOT_OWNED | 403 | Task exists but belongs to another user. |
COMMENT_INVALID_STATUS | 409 | Action not allowed in current status (e.g. cancel a finalized task). |
COMMENT_ACCOUNT_NOT_MANAGED | 409 | The target account has no current_cm_id. Wait for an order to be in progress. |
COMMENT_PLATFORM_MISMATCH | 400 | Target platform ≠ account platform. |
COMMENT_INVALID_VIDEO_URL | 400 | Could not parse the video URL. |
COMMENT_INVALID_COMMENT_URL | 400 | target_comment_url is not a recognizable TikTok comment link. |
COMMENT_NOT_A_COMMENT_URL | 400 | The link resolved to a video, not a specific comment. |
COMMENT_TARGET_PLATFORM_UNSUPPORTED | 400 | Comment targets (replies) are TikTok-only. |
COMMENT_TEXT_TOO_LONG | 400 | comment_text exceeds the platform's character limit. |
COMMENT_TEXT_EMPTY | 400 | comment_text is blank. |
COMMENT_DISPUTE_REASON_REQUIRED | 400 | Dispute call missing or short reason field. |
INSUFFICIENT_CREDITS | 402 | Not enough credits for the requested batch. |
MANAGED_ACCOUNT_TASK_BLOCKED | 409 | Coverage is inactive or the account is detached, banned, missing Coverage, or otherwise unavailable. Read details.reason, then fetch the account Coverage endpoint. |
SAVED_ACCOUNT_NOT_FOUND | 404 | saved_account_id doesn't match any of your accounts. |
SAVED_ACCOUNT_NOT_OWNED | 403 | Account belongs to someone else. |
When creating a batch, the response always succeeds at HTTP-201 even if individual rows are rejected. Inspect created / rejected to surface per-row results.
Batch behaviour vs single-shot
The POST /api/ext/comments body accepts either a single object or { "tasks": [...] }. The two paths differ in error semantics:
- Single object → if the row is rejected at validation, the endpoint returns the matching
400/409/404error code. Easier to detect failure. - Batch (
tasks: [...]) → always returns201with arejectedarray even if every row fails. The batch is partial-success — the rows that pass are still created.
What auto-verification actually checks
The verifier runs once, 30 seconds after the manager clicks "I posted it" (so the platform has time to index the new comment). It then:
- Scrapes the target video's last ~100 comments via Apify (plus replies, for comment targets).
- Filters to comments whose author handle matches the saved account. For a comment target, replies are scraped too and matched the same way (handle + text) — the reply doesn't have to sit under a specific parent comment.
- Computes Sørensen-Dice text similarity vs your
comment_text. - Decides:
- ≥ 0.95 + same handle →
verified_auto - < 0.95 + same handle (text drifted) →
pending_corrections, manager is asked to edit/repost - ≥ 0.92 + different handle (text matches but wrong account) →
pending_corrections, manager asked to delete + repost from the right account - nothing matches → manager is offered the manual confirmation modal, which moves the task into
submitted(still in flight) ormanually_confirmed(your turn)
- ≥ 0.95 + same handle →
You can fetch the full verifier timeline via GET /api/ext/comments/{id}/verifications.
Rate limits & quotas
- API rate limit: 120 req/min per key (shared with the rest of the TokPortal API).
- Batch size cap: 200 tasks per
POSTbody. - Hard deadline per task: 72 hours between creation and
submitted. Tasks that expire are auto-cancelled and credits refunded. - While TokPortal Coverage blocks execution, withheld tasks do not consume the manager's queue or expire as late work.