Approve & Dispute
When the auto-verifier can't find a comment but the manager swears they posted it, the task moves to manually_confirmed and you get a 72h window to:
- Approve — pay the manager immediately, skip the wait.
- Dispute — send a reason back to the manager so they fix it. The task flips to
pending_corrections; once they re-verify, it moves on.
If you do nothing for 72 hours, the task auto-finalizes (the manager is paid).
Approve
POST /api/ext/comments/{id}/approve
curl -X POST https://app.tokportal.com/api/ext/comments/0d8b5a3e-92c4-4111-9a7d-3e2f1a2b3c4d/approve \
-H "X-API-Key: tok_live_xxx"
Response (200):
{
"data": {
"id": "0d8b5a3e-92c4-4111-9a7d-3e2f1a2b3c4d",
"status": "finalized",
"platform": "tiktok",
"cm_payout_amount": 0.24,
"submitted_at": "2026-04-27T17:21:34Z",
"manually_confirmed_at": "2026-04-28T13:56:11Z",
"finalized_at": "2026-04-28T13:56:17Z",
"...": "..."
}
}
The payout is computed from submitted_at → manually_confirmed_at (the same speed-tier table as auto-verified tasks).
Errors
404 COMMENT_TASK_NOT_FOUND— bad ID.403 COMMENT_TASK_NOT_OWNED— belongs to another user.409 COMMENT_INVALID_STATUS— task isn't inmanually_confirmed.details.current_statustells you where it actually is.
Dispute
POST /api/ext/comments/{id}/dispute
Body:
| Field | Type | Required | Description |
|---|---|---|---|
reason | string | yes | 3–500 characters. The manager will see this verbatim and use it to fix the comment. |
curl -X POST https://app.tokportal.com/api/ext/comments/0d8b5a3e-92c4-4111-9a7d-3e2f1a2b3c4d/dispute \
-H "X-API-Key: tok_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"reason": "Cannot find the comment under the video, please re-post."
}'
Response (200):
{
"data": {
"id": "0d8b5a3e-92c4-4111-9a7d-3e2f1a2b3c4d",
"status": "pending_corrections",
"correction_required": "The client reviewed your comment and reported a problem:\n\n\"Cannot find the comment under the video, please re-post.\"\n\nPlease fix the issue and click \"I fixed it — re-check\" once done.",
"manually_confirmed_at": null,
"client_dispute_deadline_at": null,
"...": "..."
}
}
The task is now in pending_corrections. The manager fixes it and re-runs the auto-verifier — on success the task lands in verified_auto (or manually_confirmed again, if the verifier still can't see it). You can keep iterating: dispute again, approve, or just wait.
Errors
400 COMMENT_DISPUTE_REASON_REQUIRED—reasonmissing or too short.409 COMMENT_INVALID_STATUS— task isn't inmanually_confirmed.- All the standard 401/403/404s.
A complete review loop in pseudo-code
import requests, time
KEY = "tok_live_xxx"
BASE = "https://app.tokportal.com/api/ext"
def review_pending():
r = requests.get(f"{BASE}/comments?status=manually_confirmed",
headers={"X-API-Key": KEY}).json()
for task in r["data"]:
timeline = requests.get(
f"{BASE}/comments/{task['id']}/verifications",
headers={"X-API-Key": KEY}).json()["data"]
# Custom logic — e.g. auto-approve if last attempt's
# comments_scraped is 0 (likely an Apify miss, not a real no-show).
last = timeline["attempts"][-1] if timeline["attempts"] else None
if last and last["comments_scraped"] == 0:
requests.post(f"{BASE}/comments/{task['id']}/approve",
headers={"X-API-Key": KEY})
else:
requests.post(f"{BASE}/comments/{task['id']}/dispute",
headers={"X-API-Key": KEY,
"Content-Type": "application/json"},
json={"reason": "Cannot locate the comment under the video."})
review_pending()