Changelog
Public TokPortal API, SDK, CLI, MCP, and documentation changelog.
Changelog
2026-09-01
Sandbox: simulate any write for free
Credits are debited at creation and never refunded, which made the first call to an unfamiliar operation expensive to get wrong. Every non-GET operation now accepts X-TokPortal-Dry-Run: true (or dry_run: true as an MCP tool argument). The request runs the same validation and the same server-side pricing as the real call and stops before the first write.
- Nothing happens. No credits debited, no allowance slot consumed, no row written, no webhook delivered, no notification — no account manager ever sees a simulated bundle.
revealAccountCredentialsandretrieveAccountVerificationCodereturn their428policy preview and never a credential, real or invented. - Everything is real except the write. Same status, same response shape, same errors with the same
details. That is the point: you learn what an operation refuses before a real call refuses it. - The price comes back. Successful simulations carry
credits_would_charge(the real price, workspace allowance included) next tocredits_charged: 0and your unchangedcredits_remaining, plusdry_run: trueanddry_run_notice. Every response, success and error, carries theX-TokPortal-Dry-Run: trueresponse header — the only marker on an error body. - Whole flows chain. Identifiers returned by a dry run are synthetic, always starting
00000000-0000-4000-8000-, and are accepted by other dry-run calls so you can walk create → configure → publish. Send one to a real call and you getDRY_RUN_ID_IN_LIVE_REQUEST(400) instead of a confusing not-found. - Your
Idempotency-Keysurvives. A key sent with a dry run is ignored, not consumed: the real call that follows with the same key still executes. - Rate limits are not bypassed. A simulation consumes the same bucket as a real call — the server work is real even when the write is not.
Full reference: Sandbox (dry run).
Corrections to the published contract
CreateBundleResponseandCreateBulkBundlesResponsedescribedcredits_charged,credits_remainingandcost_breakdownas nested inside ametaobject. The wire format has always been flat, next todata, and the responses themselves never changed — only the schema was wrong, since its introduction on 2026-08-31. Corrected.- The MCP tool catalogue exposed the raw header name
Idempotency-Keyas a tool argument on two tools. It never did anything (the value became a query parameter the route ignores); the real argument has always beenidempotency_key. Header parameters are no longer turned into tool arguments.
tokportal-mcp 1.15.0 carries the dry_run argument on all 50 non-GET tools.
2026-08-31
Video approval: automatic again by default, and the choice is now stated
For five days every new bundle held delivered videos for manual review. Measured against real behaviour, that default served almost nobody: 619 clients with a week or more of exposure never approved or corrected a single video, so their deliveries simply waited 72 hours before finalizing themselves. The 289 clients who do review keep manual review, and their existing bundles were untouched.
auto_finalize_videosdefaults totrueagain onPOST /bundlesandPOST /bundles/bulk. A delivered video is approved on arrival and counts as final. Sendauto_finalize_videos: falseto review each delivery — approving withPOST /bundles/{id}/videos/{position}/finalizeor pushing back with the corrections endpoint. This is changeable at any time withPATCH /bundles/{id}; it is not fixed at creation.- The creation responses now say which mode you got. Both create endpoints return
review_mode:"auto"or"manual". When you omittedauto_finalize_videos, they also returnreview_mode_notice, one sentence stating that the default was applied and how to opt out. Send the field explicitly and the notice disappears — its presence means "you did not choose", never "something went wrong". - The contract describes it.
CreateBundleResponseis a real schema instead of a free-form object,CreateBulkBundlesResponseexists, and both spell out what each mode settles to. The MCP tool catalogue was regenerated accordingly:tokportal-mcp1.14.1 on npm and on thecom.tokportal/mcpregistry entry. - Unchanged: anything left in
in_reviewis still finalized automatically about 72 hours after entering it, in either mode.auto_finalize_videoshas never switched that off.
2026-08-26
Publish dates: rescheduling obeys the lead time, publishing repairs stale dates
The minimum publish lead time was only ever enforced when a date was first written. Two holes let videos reach a manager as instantly-due, same-day work. Both are closed, and the published contract — the OpenAPI descriptions, the MCP tool catalogue, this documentation — has been corrected to describe what the API actually does.
PATCH /bundles/{id}/videos/{position}now enforces the lead time. Rescheduling used to run only the 3-videos-per-day cap, so aPATCHcould move a video onto today or into the past. It now applies the same rule as configuration — today + 1 with a delivered or existing account, today + 3 while the account is still being created, compared in UTC — and rejects anything earlier withINVALID_DATEcarryingmin_days_aheadandearliest_allowed. This is the same error shape the configurePUTalready returned. If you re-date videos in bulk, especially onto the current day, this is the change most likely to turn a200into a400. The MCP toolupdateVideoand the Operator bulk date shift both funnel through this endpoint.- Publishing moves a stale date forward instead of shipping it. No publish path revalidated dates, and a draft that sat in
pending_setuplong enough published them as they were. Every publish surface —POST /bundles/{id}/publish, everyauto_publishpath (single configure, batch, CSV, on a bundle in setup or already active),POST /bundles/{id}/videos/{position}/publish,POST /bundles/{id}/videos/publish-all, and the dashboard — now moves any slot whose target date has already reached today or the past to the earliest allowed day, honouring the 3-per-day cap. Slots still in the future are not touched, and the guard never fails a publish: a call that used to succeed cannot start failing because of it. - What was moved is reported.
POST /bundles/{id}/publishand theauto_publishobject carryadjusted_videos— one{ video_id, position, previous_date, new_date }entry per moved slot — plusadjusted_videos_note. The single-video publish carriesdate_adjusted,original_date,new_dateandhint; publish-all carriesdates_adjusted,adjusted_videosandhint. The single-video and publish-all fields had been documented for a long time, but the underlying procedures never returned them and the slots published on their stale dates. They are real now. - Removed:
earliest_allowed_dateonPOST /bundles/{id}/videos/publish-all. It was documented and never returned.adjusted_videos[].new_datereplaces it and says more: because the per-day cap can spread the moved slots over several days, one "earliest allowed" day could never describe the result.
target_publish_date is a window, and the contract now says so
target_publish_date names one day and stores two: the API writes target_publish_start_date = the day you sent and target_publish_end_date = that day + 1, and the manager may post on either. 46 512 of 46 571 production video rows carry exactly that shape. Nothing about the behaviour changed — the descriptions did, because callers were reading the field as a fixed date.
- The end day is derived and cannot be chosen at configuration time. Two live REST integrations had been sending a
target_publish_end_dateonPUT /bundles/{id}/videos/{position}; the field does not exist there, so it was dropped in silence and they received a window they never asked for, behind a200. It is now refused with400 UNKNOWN_FIELDwhose message is the fix, and the same applies to atarget_publish_end_datecolumn in a CSV import (CSV_PARSE_ERROR).PATCH /bundles/{id}/videos/{position}is unchanged and remains the only endpoint that accepts an explicittarget_publish_start_date/target_publish_end_datepair. - Every video response now returns the window explicitly, as
target_publish_start_dateandtarget_publish_end_date, next to the existingtarget_publish_date(which is an alias of the start). Nothing was renamed or removed. - The CSV column reference was wrong. It listed the date column as
target_publish_date; the header the importer actually requires istarget_publish_start_date, as the downloadable templates always had it.
auto_publish: top-level on batch, per-video on a single slot
The flag exists in two places with different scopes, and the published schema had blurred them: the batch item schema inherited auto_publish from the single-slot schema through an object spread, so the spec advertised a per-item flag the batch endpoint has never accepted. An agent read the spec, sent videos[0..7].auto_publish, and got a bare UNKNOWN_FIELD that named neither the rule nor the fix.
The semantics are untouched — there is one publish attempt per call, never one per video. What changed:
- The batch item schema no longer declares
auto_publish. - Both descriptions state the scope explicitly.
- The rejection now says: "auto_publish is a top-level field on this endpoint and applies to every video in the call — move it out of videos[]."
These "real field, wrong place" refusals carry details.hints and, unlike the did-you-mean refusal, apply to every caller regardless of API_STRICT_UNKNOWN_FIELDS. The gate exists to stop a typo costing an integration its 200; here the silence was the bug. See Errors → Unknown fields.
Published tokportal-mcp 1.14.0 (91 tools, same catalogue, no signature change). No pricing changed in this release.
2026-08-22
MCP 1.13.1: the spec stops promising behaviour the code no longer has
A follow-up review of 1.13.0 found that the source of truth — the OpenAPI operation descriptions, which become openapi.json, the API reference, llms-full.txt, the npm package and the Postman collection — still described the pre-1.13.0 behaviour in a handful of places. 1.13.1 is documentation and spec fidelity: no endpoint behaviour changed except one Wave B fix that had never reached the wire (below), and no pricing changed.
configureBundleVideoreally returnsignored_fieldsnow. 1.13.0 computed the list of platform-inapplicable fields and then threw it away before answering, so the documented behaviour was not the deployed one.PUT /bundles/{id}/videos/{position}now returnsignored_fieldsandignored_fields_notenext toplatform, andPUT /bundles/{id}/videos/batchreturnsignored_fieldson each affected row. An empty or absent list is the only proof that everything you sent was stored.- Report exports no longer advertise silent coercion.
createAnalyticsReportandexportAnalyticsReportHtmldescribed malformed dates as "silently treated as no filter" and an unknowntemplateas "silently becomes executive". Both have validated since 1.13.0; the descriptions now say so, publish the template enum, and point atapplied(JSON) and theX-TokPortal-Report-*headers (HTML). uploadImageno longer claims the stored extension comes fromfilename. The declaredcontent_typenames the object;filenameis only the fallback. Readstorage_pathfor the real key.uploadImageDirectno longer claims an unrecognisedpurposeis accepted. An explicitly wrong value is refused withINVALID_FIELDfor MCP callers and reported inmeta.ignored_fieldsfor REST callers; an omittedpurposestill meanscarousel.GET /credit-costsstops advertising legacy warming.niche_warminganddeep_warmingkeep their keys so existing integrations keep parsing, but theirdescriptionandnotenow read LEGACY — not offered, and theexamples.account_with_10_videos_and_warmingquote is 3 Advanced Niche Warming targets at the workspace's effective rate instead of a niche-warming session. Advanced Niche Warming is the only warming TokPortal sells; nothing about the legacy behaviour changed.- Country codes are documented as they really are. The spec claimed
countryis a 2-letter ISO code and thatUSAis rejected, whileGET /countriesreturnsUSAfor the United States andUKfor the United Kingdom. The codes that endpoint returns are the authority;USandGBare accepted aliases resolved to them. Every page that taught the opposite has been corrected. - Every request example in the spec now validates against the schema it illustrates. Nine operations shipped examples that their own schema rejected —
target_publish_datesampled as the literal stringtarget_publish_date, an image upload withcontent_type: "video/mp4", three-target warming lists with one item, and a batch item schema (allOfover a closed object) that no request could ever satisfy. Copy-pasteable examples are how an agent learns a shape, so a failing one is a bug.
Published tokportal-mcp 1.13.1 (91 tools, same catalogue, no signature change). Upgrading is recommended but not required.
MCP 1.13.0: truthful annotations, honest descriptions, and errors instead of silent drops
Two audits of the public API and the MCP surface — one on schema fidelity, one on what an agent can actually understand from the tool catalogue — found requests that the API answered 2xx, charged for, and did not honour. This release fixes the metadata first, then the behaviour.
Annotations now describe the real risk. Published tokportal-mcp 1.13.0 (91 tools, same catalogue). Until 1.12.2 destructiveHint was derived from the verb in the operation name: it flagged 14 tools, including reversible ones like tokportal_reset_bundle_video and tokportal_unschedule_bundle_video, while every credit-spending call went unflagged. It now means one thing — this call spends credits or releases a manager payout irreversibly — plus the genuine cancel / delete / revoke operations. That is 23 tools; the twelve credit-spending operations that used to slip through now carry it. readOnlyHint stays 41, idempotentHint 47, openWorldHint 16. If your host auto-approves anything not marked destructiveHint, re-check that policy. Full list on MCP → Tool annotations.
69 of 91 tool descriptions rewritten, and the server instructions now lead with the traps rather than the branding. Rules that the JSON Schema cannot express now sit on the field they constrain: the publish lead time (today + 3 days while the account is still being created, today + 1 once it is delivered or on an existing account), the ~72-hour auto-finalize window that applies regardless of auto_finalize_videos, one bundle = one account, max 3 videos per day per bundle, storage_path vs public_url, instagram_content_type on Instagram slots, and tiktok_sound_url on TikTok carousels.
Advanced Niche Warming fails loudly instead of under-delivering. wants_advanced_warming: true is mandatory whenever advanced_warming_terms or advanced_warming_terms_count is sent, and is never inferred. Three new 400s on POST /bundles and POST /bundles/bulk:
ADVANCED_WARMING_FLAG_REQUIRED— targets sent without the flag (absent or explicitlyfalse). Previously the targets were dropped and the bundle was charged anyway.ADVANCED_WARMING_TERMS_REJECTED— a target would have been removed by cleanup (case-insensitive duplicate, or outside 2-50 characters);details.dropped_termslists them. TokPortal will not deliver or bill fewer targets than you asked for.ADVANCED_WARMING_COUNT_MISMATCH— the term list and the count state different quantities; neither silently wins.
The live case behind this: 6 terms containing 3 case-duplicates plus advanced_warming_terms_count: 9 produced 3 targets and 15 credits instead of 45. That request is now refused.
Advanced Niche Warming is the only warming TokPortal sells. wants_niche_warming, wants_deep_warming and niche_warming_instructions are marked deprecated and now carry a single sentence — kept only for accounts being re-created after a refund. Their behaviour is byte-for-byte unchanged (new deep-warming orders are still rejected with DEEP_WARMING_DEPRECATED), but they are gone from every description, example and walkthrough.
Unknown fields are reported, and refused where dropping them destroys data. A misspelled biograhpy used to be stripped during validation; because PUT /bundles/{id}/account is a full replace, the stored biography was wiped to null and the call still returned 200. The six full-replace endpoints (POST /bundles, POST /bundles/bulk, PUT /bundles/{id}/account, PUT /bundles/{id}/videos/{position}, PUT /bundles/{id}/videos/batch, PUT /bundles/{id}/warming-terms) now answer 400 UNKNOWN_FIELD with a did-you-mean. Everywhere else the request runs and the names come back in meta.ignored_fields. The refusal is gated by API_STRICT_UNKNOWN_FIELDS, currently set so that MCP callers get the 400 and REST callers get meta.ignored_fields — treat anything appearing there as a bug in your integration. Over MCP, unrecognised top-level arguments are also no longer turned into query parameters. See Errors → Unknown fields.
Analytics report exports validate instead of coercing. POST /analytics/export/reports and its HTML twin ran no validation at all. The template enum is now published (executive, agency, creator, minimal, growth, board) and an unknown value is rejected rather than silently becoming executive. Dates must be YYYY-MM-DD (a full ISO timestamp is truncated to its date); an unparseable date used to be treated as no filter and quietly widened the report to the account's entire history, and an inverted window was accepted. title, brandName and query now carry their real stored limits (120 / 80 / 120). The settings actually applied come back as applied on the JSON response and as X-TokPortal-Report-* headers on the HTML export, so white-labelling can be verified instead of assumed.
Other fidelity fixes.
- Image uploads: the required
content_typefinally names the stored object (it picks the extension; the filename is only a fallback), and the response returnsstorage_pathandbucket. Usestorage_path— notpublic_url— forcarousel_imagesandprofile_picture_url. POST /upload/image/direct: an explicitly invalidpurposeis refused or reported instead of landing in the carousel bucket. An omittedpurposestill defaults tocarousel.link_in_bioon a non-Instagram bundle is reported in the response's_warningsarray; platform-inapplicable video fields come back asignored_fields.- Analytics refresh:
includeCommentswithoutforceis refused for strict callers or reported inapplied. Comment pulse echoesapplied_filtersand says whenpostoverrode the dashboard filters. - Removed:
platformonPOST /videos/{id}/ad-code-request. It was never read, and was already being stripped, so callers still sending it are unaffected. - Partial success over MCP:
tokportal_batch_configure_bundle_videosandtokportal_import_bundle_videos_csvare now markedisErroronly when zero items succeeded while errors are present. Partial and full success are unchanged, and the REST status code did not change.
No pricing changed in this release.
2026-08-18
Distribution: MCP 1.12.1 annotations, official registry, GitHub org, PyPI/Go, plugins, docs matrix
- Published
tokportal-mcp1.12.2:openWorldHintanddestructiveHintare now precise instead of conservative — 16 tools that reach a public platform are flaggedopenWorldHint: true, and 14 tools whose effect cannot be undone (non-refundable credit debits, publishing, the credential reveal, payout-releasing finalizes) are flaggeddestructiveHint: true. Tools also carrysecuritySchemesand an OAuth scope (mcp/mcp:read). On assistant surfaces whose policy forbids handling authentication secrets,tokportal_reveal_account_credentialsandtokportal_retrieve_account_verification_codeare withheld and answer with an explanation pointing to the dashboard. - Published
tokportal-mcp1.12.1: every tool now carries MCPannotations(title,readOnlyHinton all GET tools,destructiveHinton unpublish/cancel/delete/reset/unschedule/revoke,idempotentHinton GET/PUT/DELETE,openWorldHint: false). The remote server athttps://app.tokportal.com/api/ext/mcpserves the same annotations. Hosts that honor them can auto-approve reads and prompt before destructive calls. See MCP → Tool annotations. - Listed the server on the official MCP Registry as
com.tokportal/mcp(synced to the VS Code / GitHub MCP registry); submitted to the Docker MCP Catalog, the Cline Marketplace and awesome-mcp-servers; published a Gemini CLI extension. Status is tracked on MCP → Registry listings. - Opened the tokportal GitHub organization with the public sources of
tokportal-mcp,@tokportal/node,@tokportal/cli, the Python SDK (pip install tokportal, PyPI), the Go SDK (go get github.com/tokportal/tokportal-go), quickstart examples, agent skills, a Cursor plugin and a Claude Code plugin marketplace (claude plugin marketplace add tokportal/claude-plugin). - Docs: added the Agents × jobs matrix — 25 agent hosts (Claude, Claude Code, ChatGPT, Codex, Cursor, Windsurf, Cline, OpenClaw, Hermes Agent, Gemini CLI, Goose, Perplexity, Copilot Studio, Factory Droid, n8n, Make, Zapier, Pipedream, Dify, Flowise, Langflow, CrewAI, LangChain, Vercel AI SDK, Mastra) × 12 jobs, each with the host's exact connect snippet, the
tokportal_*tool sequence, REST equivalent, parameters, example configs and FAQ. Added a developer blog with RSS. All new pages are insitemap.xml,llms.txtand the IndexNow ping. - Docs:
llms.txtnow states Instagram's documented API publishing limit as 100 posts per 24-hour moving period (previously 25).
Pricing documentation clarified (no API change)
- Credits & Pricing now lists every billable operation, including the ones that were only documented on their own endpoint page: story repost link (1 credit), instant repost as story (1 credit), ad code (7 credits), and account edit request (8 credits).
- Added an explicit grandfathering table. Grandfathering is decided per saved account from its
created_atagainst the immutable production cutover, not per workspace: accounts delivered before the cutover are never charged TokPortal Coverage and keep the prior 0-credit credential reveal, permanently. The cutover value returned byGET /credit-costsis documented for reference, and it is still the only authority. - Restated that both workspace action-price grace windows closed on August 14, 2026 at 11:00 UTC. The current standard rates are 32 credits per TikTok/Instagram account setup and 5 credits per Advanced Warming target; earlier published figures (25 credits per setup, 3 or 4 credits per warming target) are historical.
- Clarified that Advanced Niche Warming is billed per niche target, never per day — the 3-day schedule is a delivery window, not a billing unit.
- Clarified the two different 25-credit figures: the recurring 25 credits every 30 days is TokPortal Coverage per eligible saved account. The old per-bundle "comment moderation" add-on no longer exists; comment work is billed per task at 1 credit through the Comments API, with no monthly component.
- Corrected the
GET /credits/historysample payload, which still showed a 25-credit account creation line.
2026-08-16
Daily scheduling cap (max 3 videos per day per bundle)
- A bundle can now have at most 3 videos targeting the same
target_publish_dateday. The cap counts every non-cancelled video slot that already has a date on that day, whatever its status; re-configuring a video on its own current day does not count against itself. - Enforced on
PUT .../videos/{position}(configure),PUT .../videos/batch,POST .../videos/import-csv, andPATCH .../videos/{position}date updates. A 4th video on the same day is rejected with the newVIDEOS_PER_DAY_EXCEEDEDerror (HTTP 400) carryingdetails.date,details.limit, anddetails.current_count. In a batch, the first 3 rows on a given day succeed and later rows fail with per-item errors. - The dashboard's bundle video editor and CSV import apply the same rule with an inline explanation. See Configure Videos → Daily Scheduling Cap.
Ban lifecycle visibility
- Published
tokportal-mcp1.12.0 with the newtokportal_list_account_banstool (91 generated operations). The remote MCP connector serves the same updated catalogue automatically. - Added
GET /account-bans: a pollable list of validated ban reports for your delivered accounts covering the whole lifecycle —appeal_pending(platform appeal filed, account unavailable but not yet banned),appeal_accepted,appeal_refused,no_appeal_banned, and the staff commercial resolution (refund/remake/no_remakewith a machine-readablereason_codesuch astos_ban, andrefund_credits). Supportsstatus,resolution(includingpending),account_id,since(anupdated_atpolling watermark), andinclude_screenshotsfor a signed 7-day evidence URL. See Bans & Appeals. - Added the
account.ban_appeal.submittedwebhook event, emitted the moment the manager files a platform appeal. This is the first push signal that an account is unavailable — previously nothing fired until the appeal resolved days later. - Added the
account.ban_resolution.decidedwebhook event, emitted when staff decides the commercial outcome of a confirmed ban (refund,remake, orno_remake+reason_code+refund_credits). - Added a
bannedfilter toGET /accounts(true= only banned accounts,false= only healthy ones). - Added a
ban_appealblock toGET /accountsandGET /accounts/{id}responses: the latest validated ban report for the account ({ id, status, reported_at, decided_at, resolution }), so list polling alone now surfaces pending appeals and resolutions. - Existing integrations are unaffected:
account.banned,account.ban_appeal.resolved,bundle.cancelled, and the existingbanfield keep their exact shapes; the new events only reach endpoints that subscribe to them.
2026-08-11
- Prepared the compatible client releases
@tokportal/node0.1.1,@tokportal/cli0.1.2, andtokportal-mcp1.11.0. Use these versions, or a later release, for the Coverage convenience methods described below. - Clarified the credential reveal contract for existing integrations. A no-body
POST /accounts/{id}/reveal-credentialsorPOST /accounts/{id}/verification-codeis a safe policy preview: it returns428 CREDENTIALS_ACKNOWLEDGMENT_REQUIREDand does not reveal credentials, debit credits, or detach the account. The OpenAPI request body is now optional for this preview, while the accepted body requires the literal valueacknowledge_support_forfeit: trueand the exact server-providedpolicy_version. - Added ergonomic TokPortal Coverage read, pause, and exact-quote reactivation methods to the maintained Node, Python, and Go SDK sources. Added accepted credential-reveal and verification-code bodies while preserving the older no-body SDK calls as safe preview calls.
- Updated generated CLI, local MCP, and remote MCP schemas so agents can perform the no-body preview before asking a human for consent. Generated accepted examples now use
trueand the server-returned policy version placeholder. Secret-returning tools reject idempotency keys before execution so credentials and codes are never stored in a replay ledger. Remote MCP errors now include the same request ID, retry delay, and rate-limit diagnostics as local MCP. - Added explicit recovery guidance for Coverage, credential policy, idempotency, and legacy API-key errors.
managed_subscription: nullis no longer described as universal proof of grandfathering because ineligible platforms and missing pre-delivery records can also have no subscription. - Clarified durable idempotency recovery. Completed responses replay for 24 hours, while an uncertain
processingclaim never expires automatically into a second financial execution. Integrations must stop on a persistentIDEMPOTENCY_KEY_IN_PROGRESSand request support/operator reconciliation instead of changing keys. - Existing metadata, bundle, and analytics calls remain compatible. Existing no-body credential calls become safe previews rather than silent reveals. Integrations that reveal credentials must add a human confirmation step and resend the current policy version.
- TokPortal will proactively contact workspaces with active API-key records in the retired legacy credential format. Those old hashes cannot be identified safely during a request without an expensive pre-authentication scan, so integrations must not expect a dedicated runtime error code. Generate a current key, deploy it, verify successful requests, and only then revoke the old key. If an old key is no longer recognized, the API returns the ordinary
AUTH_INVALID_KEYresponse.
Integration migration checklist
- Keep all existing read and bundle calls unchanged.
- Read
managed_subscriptionfrom account details or call the dedicated Coverage endpoint. Treatlapsedandcancelledas blocked states that need an exact-quote reactivation; treat terminal states as non-recoverable. - For reactivation, copy the latest quote fields exactly. Show the amount even when it is 0, send an
Idempotency-Key, and fetch a new quote onQUOTE_CHANGED. - For credentials or verification codes, first call without a body. Display the complete 428 disclosure to the account owner. Only after confirmation, send
acknowledge_support_forfeit: trueand the exact returnedpolicy_version, without an idempotency key. OnCREDENTIAL_REVEAL_QUOTE_CHANGED, obtain a new preview and fresh confirmation. - Log
X-TokPortal-Request-ID, the machine error code, and your own account or job correlation ID. Never log credentials, verification codes, API keys, or accepted disclosure payloads containing secret results. - Upgrade to the package release whose changelog contains this entry before relying on the convenience methods. Until that release is published, use the raw HTTP endpoints or a pinned reviewed SDK source commit.
Managed pricing production cutover
- Added cohort-aware pricing from the actual production migration cutover. Workspaces without a paid credit acquisition before that timestamp move immediately to 32 credits for account setup, 5 per Advanced Niche Warming target, and 150 for a first credential or verification-code reveal. Workspaces with a paid acquisition before the cutover keep 25, 3, and 0 credits until August 14, 2026 at 11:00 UTC, then move to 32, 5, and 150.
- Added TokPortal Coverage for eligible TikTok and Instagram saved accounts. The production migration records the authoritative account cutoff. Every eligible saved account that already exists at that instant remains permanently grandfathered and returns
managed_subscription: null; ineligible platforms or accounts without a Coverage record can also returnnull. - Checkout charges only the workspace-effective setup price, 25 credits during a pre-cutover paying workspace's legacy window or 32 credits after transition. No Coverage period is charged at checkout. For a non-grandfathered saved account created after the migration cutover, Coverage starts at account review submission even when that workspace keeps legacy action prices until August 14. The first 30 days are included, and the first automatic 25-credit debit on day 30 covers the next 30 days. Every successful renewal appears in credit transaction history. Coverage is unique per saved account, independent from bundle lifecycle, and never refunded.
- Added failed-payment and manual-pause task blocking, arrears tracking, exact reactivation pricing, and atomic task rescheduling after payment. Completed or cancelled bundles do not end Coverage. Bundles with no newly scheduled or posted video for 60 days are archived from the manager workspace without changing Coverage.
- Corrected reactivation pricing so an account resumed inside its already paid or included period costs 0 credits. Later reactivation pays only the exact unpaid periods, preserves the original billing anchor, and rejects stale price confirmations without a debit.
- Added confirmed-ban handling: a confirmed banned-account refund or eligible credit restoration changes Coverage to terminal
ended_ban, prevents future renewals, and emitssubscription.endedalongside the applicable credit event. - The workspace-effective price for credential reveal and verification-code retrieval is 150 credits from the production cutover for workspaces without a prior paid credit acquisition. A pre-cutover paying workspace remains at 0 credits until August 14 at 11:00 UTC. An admin-approved Account Owning Fee agreement still replaces the one-time charge. The post-transition reveal permanently detaches the account from TokPortal. Versioned policy acceptance, debit and shutdown are atomic. Historical reveals remain grandfathered.
- Account Owning Fee activation now requires admin approval after the 25-account and 25-percent-revealed threshold. Pending workspaces retain the 150-credit reveal option. After approval, reveal activates or uses the stored agreement rate instead. Existing legacy agreements remain at $10 per account every 30 days; new approvals use $15.
- Confirmed eligible ban restoration covers account setup, warming and unused video-slot credits when Coverage was active at the ban occurrence, the account was never revealed, and the claim is opened within 15 days under the Ban and Replacement Policy. Restored credits expire after 60 days; Coverage periods are never refunded.
- Added the stable technical
managed_subscriptionfield toGET /accounts/{id}and dedicated TokPortal Coverage read, pause, and reactivation endpoints across the public API, generated SDKs, CLI, local MCP and remote MCP. - Added
subscription.renewed,subscription.lapsed,subscription.cancelled,subscription.reactivated,subscription.ended,bundle.archived,account.revealedandcredits.restoredwebhook events.
2026-07-16
- Clarified the account and video review lifecycle: an account listing or video listing left in
in_reviewwithout user review is automatically moved tofinalizedafter 72 hours.
2026-07-12
- Ban visibility end-to-end. When an account ban is validated (manager report with no appeal, refused platform appeal, or staff confirmation), TokPortal now cancels every active bundle/order on the account and pushes the signal everywhere:
- New
bundle.cancelledwebhook event — fired for each cancelled bundle withreason,cancelled_at,account_banned,saved_account_id, andusername. See Webhooks. - Documented the existing
account.bannedandaccount.ban_appeal.resolvedwebhook events (emitted since the CM ban-report flow shipped). GET /accountsandGET /accounts/:idnow returnbannedand abanobject (reason,banned_at, plusappeal_statusand a signedscreenshot_urlon the detail response). Banned accounts stay visible.GET /bundlesandGET /bundles/:idnow returncancelled_atandcancelled_reason.409 BUNDLE_INVALID_STATUSon publish calls now includescancelled_at/cancelled_reasonwhen the bundle is cancelled, so the error itself tells you why.- Documented the
cancelledbundle status in the bundle lifecycle.
- New
2026-07-10
- Advanced Niche Warming was introduced at 3 credits per niche target, with 3-30 targets in multiples of 3. The August 2026 transition updates the rate to 5 credits. New bundle fields
wants_advanced_warming+advanced_warming_terms/advanced_warming_terms_countare available onPOST /bundlesandPOST /bundles/bulk. See the Advanced Niche Warming guide. - Deferred target configuration: buy the target count at creation (
advanced_warming_terms_count) and write the targets afterwards viaPUT /bundles/:id/warming-termswithadvanced_warming_terms(one-shot; must match the purchased count exactly). Warming starts at configuration when the account/order is already active, else at submission/accept. Count-only purchases never configured within 14 days are auto-cancelled and fully refunded. - Warming task spec: each recording opens on the account profile (handle visible), then the manager searches the target, watches videos from the results, engages with them (likes/saves) and leaves a comment. Every recording is verified before it counts.
- New endpoints:
PUT /bundles/:id/warming-terms,POST /accounts/:id/rewarm,GET /accounts/:id/warming-sessions,GET /warming-sessions/:id,POST /warming/generate-terms(free AI target generation). - New webhooks:
warming.session_started,warming.term_verified,warming.session_completed. Bundle payloads exposeadvanced_warming_terms_count; account payloads gain anadvanced_warmingsummary (withterms_configured). - New error codes:
ADVANCED_WARMING_PLATFORM,ADVANCED_WARMING_TERMS,WARMING_TERMS_ALREADY_SET,WARMING_SESSION_NOT_FOUND,REWARM_NO_ACTIVE_ORDER,REWARM_ALREADY_ACTIVE,TERM_GENERATION_FAILED,AI_UNAVAILABLE. - Deep warming is discontinued:
wants_deep_warming: trueis now rejected withDEEP_WARMING_DEPRECATED(in-flight orders keep working). Niche warming is review-based and will be deprecated in favor of Advanced Niche Warming.
2026-06-19
- Added the
account.remadewebhook event, emitted when an account is remade after a ban or loss. Payload includesbundle_id,external_ref,old_username,new_username,old_account_id,reason,mode,remade_count, andremade_at. - Added a
remadeobject to bundle responses (GET /bundlesandGET /bundles/:id) withwas_remade,remade_count,old_username, andremade_at. - Added
bundle_idandexternal_refto the account detail response (GET /accounts/:id) — stable anchors for correlating an account across remakes (the saved account id itself changes on a remake). - Added
saved_account_idtoGET /bundles/:id(top-level and insideaccount) and to theaccount.*webhook payloads (populated fromaccount.in_reviewonward) — the clean way to obtain the real/new saved-account id for a bundle, including after a remake. Documented the account-listing vs saved-account distinction.
2026-05-25
- Added first-class OpenAPI assets at
/openapi.jsonand/openapi.yaml. - Added
llms.txtandllms-full.txtfor LLM ingestion. - Added
ai-context.jsonanddeveloper-ecosystem.jsonfor structured AI and developer ecosystem ingestion. - Documented the canonical API key format:
sk_followed by 64 lowercase hex characters, stored as SHA-256, shown once at creation. - Added public Node / TypeScript SDK package:
@tokportal/node. - Added public CLI package:
@tokportal/cli. - Added published public MCP package:
tokportal-mcp. - Deferred Python, Go, Ruby, Java, PHP, .NET, and Rust SDKs until their registry or repository release path is ready.
- Added package distribution metadata and a public ecosystem manifest with release status for SDK, CLI, and MCP surfaces.
- Added structured SDK error handling docs for Node, including request IDs and retryability helpers.
- Added SDK idempotency examples.
- Added
X-TokPortal-Clientidentification headers across generated SDKs, CLI, and MCP server. - Added public API version and stability response headers:
X-TokPortal-API-Version: 2026-05-25andX-TokPortal-API-Stability: stable. - Added
X-TokPortal-Request-IDresponse headers across public API responses and SDK error parsing for support/debug correlation. - Added request ID persistence in API logs and per-key usage inspection in the Developer Portal.
- Added API key environment tags, read/write scopes, optional expiry dates, and scope-aware auth errors without changing existing keys.
- Added API key metadata editing and audit trail events for creation, updates, rotation, rotation replacements, and revocation.
- Added
X-RateLimit-Limit,X-RateLimit-Remaining,X-RateLimit-Reset, andRetry-Afterheaders for authenticated public API requests. - Added SDK error metadata for rate limiting and backoff: Node
retryAfterSecondsandrateLimit. - Added Node webhook signature helper docs plus manual HMAC verification examples.
- Added CLI and MCP error diagnostics with
request_id,retry_after_seconds, andrate_limitmetadata. - Added the Versioning & Stability docs page with backwards compatibility and deprecation policy.
- Added public webhook endpoint registry at
/webhookswith emitted bundle, account, video, and test events, delivery logs, and test delivery support. - Added the public webhook event catalog at
/webhooks/eventswith event availability, payload schemas, example payloads, delivery headers, and signature metadata. - Added manual webhook delivery retry at
/webhooks/{id}/deliveries/{delivery_id}/retry, preserving the event ID and recording a new signed attempt. - Added persisted
Idempotency-Keyhandling for mutating API requests, including replay detection and conflict errors. - Expanded OpenAPI/SDK coverage for Analytics v2 contract/export/report endpoints and media upload helpers, including direct upload and image import from URL.
- Expanded OpenAPI/SDK coverage for bundle updates, account/video review lifecycle actions, video download fixes, analytics refresh checks, raw analytics snapshots, and standalone HTML report export.
- Added generated OpenAPI
x-codeSamplesfor every operation across curl, Node, Python, and Go. - Added a generated
/api-referencedocs page from OpenAPI so every operation and code sample is searchable and included inllms-full.txt. - Kept legacy API key validation compatible while new keys use the canonical
sk_format.